Reland: Change input injection security model

This reverts commit 6b3c6cae3f.

Changes from revert:
- Explicitly maintain the same behavior as previous versions of Android,
  where injection is allowed into all window if the caller has the
  INJECT_EVENTS permission, even if it is targeting a certain uid. This
  fallback will be removed separately in b/228161340.
- Clear the calling identity before checking if the injection source has
  a permisssion to avoid hitting blanket-checks in PackageManager that
  denys permissions in some cases, such as if the permission check call
  is coming from an instant app (b/228319794).
- Leave the existing IInputManager#injectInputEvent method signature
  unchanged, and add a new input method injectInputEventToTarget to
  perform targeted injection. This makes it so that tools or apps
  relying on the @UnsupportedAppUsage injection method will not be
  broken by a change in the method's signature (b/228583652).

Bug: 207667844
Bug: 194952792
Test: Run tests affected by b/228319794 and its duplicates using
cts-tradefed, so that they are also run as instant apps.
Test: Test scrcpy tool works locally

Change-Id: I503e4f087501e3aecf2c18b4b1d27e51ecda59ac
Merged-In: I503e4f087501e3aecf2c18b4b1d27e51ecda59ac
This commit is contained in:
Prabir Pradhan
2022-04-11 17:31:41 +00:00
parent 0656f1c02a
commit 0f9ea02be0
10 changed files with 216 additions and 112 deletions

View File

@@ -568,14 +568,15 @@ public abstract class ActivityManagerInternal {
public abstract void unregisterProcessObserver(IProcessObserver processObserver); public abstract void unregisterProcessObserver(IProcessObserver processObserver);
/** /**
* Checks if there is an unfinished instrumentation that targets the given uid. * Gets the uid of the instrumentation source if there is an unfinished instrumentation that
* targets the given uid.
* *
* @param uid The uid to be checked for * @param uid The uid to be checked for
* *
* @return True, if there is an instrumentation whose target application uid matches the given * @return the uid of the instrumentation source, if there is an instrumentation whose target
* uid, false otherwise * application uid matches the given uid, and {@link android.os.Process#INVALID_UID} otherwise.
*/ */
public abstract boolean isUidCurrentlyInstrumented(int uid); public abstract int getInstrumentationSourceUid(int uid);
/** Is this a device owner app? */ /** Is this a device owner app? */
public abstract boolean isDeviceOwner(int uid); public abstract boolean isDeviceOwner(int uid);

View File

@@ -1058,10 +1058,11 @@ public class Instrumentation {
} }
/** /**
* Sends the key events corresponding to the text to the app being * Sends the key events that result in the given text being typed into the currently focused
* instrumented. * window, and waits for it to be processed.
* *
* @param text The text to be sent. * @param text The text to be sent.
* @see #sendKeySync(KeyEvent)
*/ */
public void sendStringSync(String text) { public void sendStringSync(String text) {
if (text == null) { if (text == null) {
@@ -1084,11 +1085,12 @@ public class Instrumentation {
} }
/** /**
* Send a key event to the currently focused window/view and wait for it to * Sends a key event to the currently focused window, and waits for it to be processed.
* be processed. Finished at some point after the recipient has returned * <p>
* from its event processing, though it may <em>not</em> have completely * This method blocks until the recipient has finished handling the event. Note that the
* finished reacting from the event -- for example, if it needs to update * recipient may <em>not</em> have completely finished reacting from the event when this method
* its display as a result, it may still be in the process of doing that. * returns. For example, it may still be in the process of updating its display or UI contents
* upon reacting to the injected event.
* *
* @param event The event to send to the current focus. * @param event The event to send to the current focus.
*/ */
@@ -1116,34 +1118,42 @@ public class Instrumentation {
} }
/** /**
* Sends an up and down key event sync to the currently focused window. * Sends up and down key events with the given key code to the currently focused window, and
* waits for it to be processed.
* *
* @param key The integer keycode for the event. * @param keyCode The key code for the events to send.
* @see #sendKeySync(KeyEvent)
*/ */
public void sendKeyDownUpSync(int key) { public void sendKeyDownUpSync(int keyCode) {
sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, key));
sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, key));
}
/**
* Higher-level method for sending both the down and up key events for a
* particular character key code. Equivalent to creating both KeyEvent
* objects by hand and calling {@link #sendKeySync}. The event appears
* as if it came from keyboard 0, the built in one.
*
* @param keyCode The key code of the character to send.
*/
public void sendCharacterSync(int keyCode) {
sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, keyCode)); sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
} }
/** /**
* Dispatch a pointer event. Finished at some point after the recipient has * Sends up and down key events with the given key code to the currently focused window, and
* returned from its event processing, though it may <em>not</em> have * waits for it to be processed.
* completely finished reacting from the event -- for example, if it needs * <p>
* to update its display as a result, it may still be in the process of * Equivalent to {@link #sendKeyDownUpSync(int)}.
* doing that. *
* @param keyCode The key code of the character to send.
* @see #sendKeySync(KeyEvent)
*/
public void sendCharacterSync(int keyCode) {
sendKeyDownUpSync(keyCode);
}
/**
* Dispatches a pointer event into a window owned by the instrumented application, and waits for
* it to be processed.
* <p>
* If the motion event being injected is targeted at a window that is not owned by the
* instrumented application, the input injection will fail. See {@link #getUiAutomation()} for
* injecting events into all windows.
* <p>
* This method blocks until the recipient has finished handling the event. Note that the
* recipient may <em>not</em> have completely finished reacting from the event when this method
* returns. For example, it may still be in the process of updating its display or UI contents
* upon reacting to the injected event.
* *
* @param event A motion event describing the pointer action. (As noted in * @param event A motion event describing the pointer action. (As noted in
* {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
@@ -1155,10 +1165,10 @@ public class Instrumentation {
event.setSource(InputDevice.SOURCE_TOUCHSCREEN); event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
} }
syncInputTransactionsAndInjectEvent(event); syncInputTransactionsAndInjectEventIntoSelf(event);
} }
private void syncInputTransactionsAndInjectEvent(MotionEvent event) { private void syncInputTransactionsAndInjectEventIntoSelf(MotionEvent event) {
final boolean syncBefore = event.getAction() == MotionEvent.ACTION_DOWN final boolean syncBefore = event.getAction() == MotionEvent.ACTION_DOWN
|| event.isFromSource(InputDevice.SOURCE_MOUSE); || event.isFromSource(InputDevice.SOURCE_MOUSE);
final boolean syncAfter = event.getAction() == MotionEvent.ACTION_UP; final boolean syncAfter = event.getAction() == MotionEvent.ACTION_UP;
@@ -1169,8 +1179,9 @@ public class Instrumentation {
.syncInputTransactions(true /*waitForAnimations*/); .syncInputTransactions(true /*waitForAnimations*/);
} }
// Direct the injected event into windows owned by the instrumentation target.
InputManager.getInstance().injectInputEvent( InputManager.getInstance().injectInputEvent(
event, InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH); event, InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH, Process.myUid());
if (syncAfter) { if (syncAfter) {
WindowManagerGlobal.getWindowManagerService() WindowManagerGlobal.getWindowManagerService()
@@ -1182,19 +1193,21 @@ public class Instrumentation {
} }
/** /**
* Dispatch a trackball event. Finished at some point after the recipient has * Dispatches a trackball event into the currently focused window, and waits for it to be
* returned from its event processing, though it may <em>not</em> have * processed.
* completely finished reacting from the event -- for example, if it needs * <p>
* to update its display as a result, it may still be in the process of * This method blocks until the recipient has finished handling the event. Note that the
* doing that. * recipient may <em>not</em> have completely finished reacting from the event when this method
* * returns. For example, it may still be in the process of updating its display or UI contents
* upon reacting to the injected event.
*
* @param event A motion event describing the trackball action. (As noted in * @param event A motion event describing the trackball action. (As noted in
* {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use
* {@link SystemClock#uptimeMillis()} as the timebase. * {@link SystemClock#uptimeMillis()} as the timebase.
*/ */
public void sendTrackballEventSync(MotionEvent event) { public void sendTrackballEventSync(MotionEvent event) {
validateNotAppThread(); validateNotAppThread();
if ((event.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) == 0) { if (!event.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
event.setSource(InputDevice.SOURCE_TRACKBALL); event.setSource(InputDevice.SOURCE_TRACKBALL);
} }
InputManager.getInstance().injectInputEvent(event, InputManager.getInstance().injectInputEvent(event,

View File

@@ -57,11 +57,16 @@ interface IInputManager {
// Temporarily changes the pointer speed. // Temporarily changes the pointer speed.
void tryPointerSpeed(int speed); void tryPointerSpeed(int speed);
// Injects an input event into the system. To inject into windows owned by other // Injects an input event into the system. The caller must have the INJECT_EVENTS permssion.
// applications, the caller must have the INJECT_EVENTS permission. // This method exists only for compatibility purposes and may be removed in a future release.
@UnsupportedAppUsage @UnsupportedAppUsage
boolean injectInputEvent(in InputEvent ev, int mode); boolean injectInputEvent(in InputEvent ev, int mode);
// Injects an input event into the system. The caller must have the INJECT_EVENTS permission.
// The caller can target windows owned by a certain UID by providing a valid UID, or by
// providing {@link android.os.Process#INVALID_UID} to target all windows.
boolean injectInputEventToTarget(in InputEvent ev, int mode, int targetUid);
VerifiedInputEvent verifyInputEvent(in InputEvent ev); VerifiedInputEvent verifyInputEvent(in InputEvent ev);
// Calibrate input device position // Calibrate input device position

View File

@@ -45,6 +45,7 @@ import android.os.IVibratorStateListener;
import android.os.InputEventInjectionSync; import android.os.InputEventInjectionSync;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.Process;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException; import android.os.ServiceManager.ServiceNotFoundException;
@@ -1107,14 +1108,58 @@ public final class InputManager {
} }
} }
/**
* Injects an input event into the event system, targeting windows owned by the provided uid.
*
* If a valid targetUid is provided, the system will only consider injecting the input event
* into windows owned by the provided uid. If the input event is targeted at a window that is
* not owned by the provided uid, input injection will fail and a RemoteException will be
* thrown.
*
* The synchronization mode determines whether the method blocks while waiting for
* input injection to proceed.
* <p>
* Requires the {@link android.Manifest.permission.INJECT_EVENTS} permission.
* </p><p>
* Make sure you correctly set the event time and input source of the event
* before calling this method.
* </p>
*
* @param event The event to inject.
* @param mode The synchronization mode. One of:
* {@link android.os.InputEventInjectionSync.NONE},
* {@link android.os.InputEventInjectionSync.WAIT_FOR_RESULT}, or
* {@link android.os.InputEventInjectionSync.WAIT_FOR_FINISHED}.
* @param targetUid The uid to target, or {@link android.os.Process#INVALID_UID} to target all
* windows.
* @return True if input event injection succeeded.
*
* @hide
*/
@RequiresPermission(Manifest.permission.INJECT_EVENTS)
public boolean injectInputEvent(InputEvent event, int mode, int targetUid) {
if (event == null) {
throw new IllegalArgumentException("event must not be null");
}
if (mode != InputEventInjectionSync.NONE
&& mode != InputEventInjectionSync.WAIT_FOR_FINISHED
&& mode != InputEventInjectionSync.WAIT_FOR_RESULT) {
throw new IllegalArgumentException("mode is invalid");
}
try {
return mIm.injectInputEventToTarget(event, mode, targetUid);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
/** /**
* Injects an input event into the event system on behalf of an application. * Injects an input event into the event system on behalf of an application.
* The synchronization mode determines whether the method blocks while waiting for * The synchronization mode determines whether the method blocks while waiting for
* input injection to proceed. * input injection to proceed.
* <p> * <p>
* Requires {@link android.Manifest.permission.INJECT_EVENTS} to inject into * Requires the {@link android.Manifest.permission.INJECT_EVENTS} permission.
* windows that are owned by other applications.
* </p><p> * </p><p>
* Make sure you correctly set the event time and input source of the event * Make sure you correctly set the event time and input source of the event
* before calling this method. * before calling this method.
@@ -1129,22 +1174,10 @@ public final class InputManager {
* *
* @hide * @hide
*/ */
@RequiresPermission(Manifest.permission.INJECT_EVENTS)
@UnsupportedAppUsage @UnsupportedAppUsage
public boolean injectInputEvent(InputEvent event, int mode) { public boolean injectInputEvent(InputEvent event, int mode) {
if (event == null) { return injectInputEvent(event, mode, Process.INVALID_UID);
throw new IllegalArgumentException("event must not be null");
}
if (mode != InputEventInjectionSync.NONE
&& mode != InputEventInjectionSync.WAIT_FOR_FINISHED
&& mode != InputEventInjectionSync.WAIT_FOR_RESULT) {
throw new IllegalArgumentException("mode is invalid");
}
try {
return mIm.injectInputEvent(event, mode);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
} }
/** /**

View File

@@ -17195,17 +17195,17 @@ public class ActivityManagerService extends IActivityManager.Stub
} }
@Override @Override
public boolean isUidCurrentlyInstrumented(int uid) { public int getInstrumentationSourceUid(int uid) {
synchronized (mProcLock) { synchronized (mProcLock) {
for (int i = mActiveInstrumentation.size() - 1; i >= 0; i--) { for (int i = mActiveInstrumentation.size() - 1; i >= 0; i--) {
ActiveInstrumentation activeInst = mActiveInstrumentation.get(i); ActiveInstrumentation activeInst = mActiveInstrumentation.get(i);
if (!activeInst.mFinished && activeInst.mTargetInfo != null if (!activeInst.mFinished && activeInst.mTargetInfo != null
&& activeInst.mTargetInfo.uid == uid) { && activeInst.mTargetInfo.uid == uid) {
return true; return activeInst.mSourceUid;
} }
} }
} }
return false; return INVALID_UID;
} }
@Override @Override

View File

@@ -2368,7 +2368,8 @@ public class AppOpsService extends IAppOpsService.Stub {
ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class); ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
boolean isSelfRequest = (filter & FILTER_BY_UID) != 0 && uid == Binder.getCallingUid(); boolean isSelfRequest = (filter & FILTER_BY_UID) != 0 && uid == Binder.getCallingUid();
if (!isSelfRequest) { if (!isSelfRequest) {
boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid()); boolean isCallerInstrumented =
ami.getInstrumentationSourceUid(Binder.getCallingUid()) != Process.INVALID_UID;
boolean isCallerSystem = Binder.getCallingPid() == Process.myPid(); boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
boolean isCallerPermissionController; boolean isCallerPermissionController;
try { try {
@@ -6924,7 +6925,8 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override @Override
public @Nullable RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage() { public @Nullable RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage() {
ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class); ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid()); boolean isCallerInstrumented =
ami.getInstrumentationSourceUid(Binder.getCallingUid()) != Process.INVALID_UID;
boolean isCallerSystem = Binder.getCallingPid() == Process.myPid(); boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
if (!isCallerSystem && !isCallerInstrumented) { if (!isCallerSystem && !isCallerInstrumented) {
return null; return null;

View File

@@ -20,6 +20,7 @@ import static android.view.KeyEvent.KEYCODE_UNKNOWN;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
@@ -860,6 +861,19 @@ public class InputManagerService extends IInputManager.Stub
@Override // Binder call @Override // Binder call
public boolean injectInputEvent(InputEvent event, int mode) { public boolean injectInputEvent(InputEvent event, int mode) {
return injectInputEventToTarget(event, mode, Process.INVALID_UID);
}
@Override // Binder call
public boolean injectInputEventToTarget(InputEvent event, int mode, int targetUid) {
if (!checkCallingPermission(android.Manifest.permission.INJECT_EVENTS,
"injectInputEvent()", true /*checkInstrumentationSource*/)) {
throw new SecurityException(
"Injecting input events requires the caller (or the source of the "
+ "instrumentation, if any) to have the INJECT_EVENTS permission.");
}
// We are not checking if targetUid matches the callingUid, since having the permission
// already means you can inject into any window.
Objects.requireNonNull(event, "event must not be null"); Objects.requireNonNull(event, "event must not be null");
if (mode != InputEventInjectionSync.NONE if (mode != InputEventInjectionSync.NONE
&& mode != InputEventInjectionSync.WAIT_FOR_FINISHED && mode != InputEventInjectionSync.WAIT_FOR_FINISHED
@@ -868,22 +882,39 @@ public class InputManagerService extends IInputManager.Stub
} }
final int pid = Binder.getCallingPid(); final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
final boolean injectIntoUid = targetUid != Process.INVALID_UID;
final int result; final int result;
try { try {
result = mNative.injectInputEvent(event, pid, uid, mode, result = mNative.injectInputEvent(event, injectIntoUid,
INJECTION_TIMEOUT_MILLIS, WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT); targetUid, mode, INJECTION_TIMEOUT_MILLIS,
WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
} finally { } finally {
Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident);
} }
switch (result) { switch (result) {
case InputEventInjectionResult.PERMISSION_DENIED:
Slog.w(TAG, "Input event injection from pid " + pid + " permission denied.");
throw new SecurityException(
"Injecting to another application requires INJECT_EVENTS permission");
case InputEventInjectionResult.SUCCEEDED: case InputEventInjectionResult.SUCCEEDED:
return true; return true;
case InputEventInjectionResult.TARGET_MISMATCH:
if (!injectIntoUid) {
throw new IllegalStateException("Injection should not result in TARGET_MISMATCH"
+ " when it is not targeted into to a specific uid.");
}
// TODO(b/228161340): Remove the fallback of targeting injection into all windows
// when the caller has the injection permission.
// Explicitly maintain the same behavior as previous versions of Android, where
// injection is allowed into all windows if the caller has the INJECT_EVENTS
// permission, even if it is targeting a certain uid.
if (checkCallingPermission(android.Manifest.permission.INJECT_EVENTS,
"injectInputEvent-target-mismatch-fallback")) {
Slog.w(TAG, "Targeted input event was not directed at a window owned by uid "
+ targetUid + ". Falling back to injecting into all windows.");
return injectInputEventToTarget(event, mode, Process.INVALID_UID);
}
throw new IllegalArgumentException(
"Targeted input event injection from pid " + pid
+ " was not directed at a window owned by uid "
+ targetUid + ".");
case InputEventInjectionResult.TIMED_OUT: case InputEventInjectionResult.TIMED_OUT:
Slog.w(TAG, "Input event injection from pid " + pid + " timed out."); Slog.w(TAG, "Input event injection from pid " + pid + " timed out.");
return false; return false;
@@ -2780,8 +2811,12 @@ public class InputManagerService extends IInputManager.Stub
} }
} }
} }
private boolean checkCallingPermission(String permission, String func) { private boolean checkCallingPermission(String permission, String func) {
return checkCallingPermission(permission, func, false /*checkInstrumentationSource*/);
}
private boolean checkCallingPermission(String permission, String func,
boolean checkInstrumentationSource) {
// Quick check: if the calling permission is me, it's all okay. // Quick check: if the calling permission is me, it's all okay.
if (Binder.getCallingPid() == Process.myPid()) { if (Binder.getCallingPid() == Process.myPid()) {
return true; return true;
@@ -2790,6 +2825,28 @@ public class InputManagerService extends IInputManager.Stub
if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) { if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
return true; return true;
} }
if (checkInstrumentationSource) {
final ActivityManagerInternal ami =
LocalServices.getService(ActivityManagerInternal.class);
Objects.requireNonNull(ami, "ActivityManagerInternal should not be null.");
final int instrumentationUid = ami.getInstrumentationSourceUid(Binder.getCallingUid());
if (instrumentationUid != Process.INVALID_UID) {
// Clear the calling identity when checking if the instrumentation source has
// permission because PackageManager will deny all permissions to some callers,
// such as instant apps.
final long token = Binder.clearCallingIdentity();
try {
if (mContext.checkPermission(permission, -1 /*pid*/, instrumentationUid)
== PackageManager.PERMISSION_GRANTED) {
return true;
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
String msg = "Permission Denial: " + func + " from pid=" String msg = "Permission Denial: " + func + " from pid="
+ Binder.getCallingPid() + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid() + ", uid=" + Binder.getCallingUid()
@@ -3033,13 +3090,6 @@ public class InputManagerService extends IInputManager.Stub
return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags); return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
} }
// Native callback.
@SuppressWarnings("unused")
private boolean checkInjectEventsPermission(int injectorPid, int injectorUid) {
return mContext.checkPermission(android.Manifest.permission.INJECT_EVENTS,
injectorPid, injectorUid) == PackageManager.PERMISSION_GRANTED;
}
// Native callback. // Native callback.
@SuppressWarnings("unused") @SuppressWarnings("unused")
private void onPointerDownOutsideFocus(IBinder touchedToken) { private void onPointerDownOutsideFocus(IBinder touchedToken) {
@@ -3501,12 +3551,17 @@ public class InputManagerService extends IInputManager.Stub
@Override @Override
public void sendInputEvent(InputEvent event, int policyFlags) { public void sendInputEvent(InputEvent event, int policyFlags) {
if (!checkCallingPermission(android.Manifest.permission.INJECT_EVENTS,
"sendInputEvent()")) {
throw new SecurityException(
"The INJECT_EVENTS permission is required for injecting input events.");
}
Objects.requireNonNull(event, "event must not be null"); Objects.requireNonNull(event, "event must not be null");
synchronized (mInputFilterLock) { synchronized (mInputFilterLock) {
if (!mDisconnected) { if (!mDisconnected) {
mNative.injectInputEvent(event, 0, 0, mNative.injectInputEvent(event, false /* injectIntoUid */, -1 /* uid */,
InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0 /* timeout */,
policyFlags | WindowManagerPolicy.FLAG_FILTERED); policyFlags | WindowManagerPolicy.FLAG_FILTERED);
} }
} }

View File

@@ -70,7 +70,18 @@ public interface NativeInputManagerService {
void setBlockUntrustedTouchesMode(int mode); void setBlockUntrustedTouchesMode(int mode);
int injectInputEvent(InputEvent event, int pid, int uid, int syncMode, /**
* Inject an input event into the system.
*
* @param event the input event to inject
* @param injectIntoUid true if the event should target windows owned by uid, false otherwise
* @param uid the uid whose windows should be targeted, if any
* @param syncMode {@link android.os.InputEventInjectionSync}
* @param timeoutMillis timeout to wait for input injection to complete, in milliseconds
* @param policyFlags defined in {@link android.view.WindowManagerPolicyConstants}
* @return {@link android.os.InputEventInjectionResult}
*/
int injectInputEvent(InputEvent event, boolean injectIntoUid, int uid, int syncMode,
int timeoutMillis, int policyFlags); int timeoutMillis, int policyFlags);
VerifiedInputEvent verifyInputEvent(InputEvent event); VerifiedInputEvent verifyInputEvent(InputEvent event);
@@ -240,7 +251,8 @@ public interface NativeInputManagerService {
public native void setBlockUntrustedTouchesMode(int mode); public native void setBlockUntrustedTouchesMode(int mode);
@Override @Override
public native int injectInputEvent(InputEvent event, int pid, int uid, int syncMode, public native int injectInputEvent(InputEvent event, boolean injectIntoUid, int uid,
int syncMode,
int timeoutMillis, int policyFlags); int timeoutMillis, int policyFlags);
@Override @Override

View File

@@ -410,7 +410,8 @@ public final class LogcatManagerService extends SystemService {
} }
private void processNewLogAccessRequest(LogAccessClient client) { private void processNewLogAccessRequest(LogAccessClient client) {
boolean isInstrumented = mActivityManagerInternal.isUidCurrentlyInstrumented(client.mUid); boolean isInstrumented = mActivityManagerInternal.getInstrumentationSourceUid(client.mUid)
!= android.os.Process.INVALID_UID;
// The instrumented apks only run for testing, so we don't check user permission. // The instrumented apks only run for testing, so we don't check user permission.
if (isInstrumented) { if (isInstrumented) {

View File

@@ -106,7 +106,6 @@ static struct {
jmethodID interceptMotionBeforeQueueingNonInteractive; jmethodID interceptMotionBeforeQueueingNonInteractive;
jmethodID interceptKeyBeforeDispatching; jmethodID interceptKeyBeforeDispatching;
jmethodID dispatchUnhandledKey; jmethodID dispatchUnhandledKey;
jmethodID checkInjectEventsPermission;
jmethodID onPointerDisplayIdChanged; jmethodID onPointerDisplayIdChanged;
jmethodID onPointerDownOutsideFocus; jmethodID onPointerDownOutsideFocus;
jmethodID getVirtualKeyQuietTimeMillis; jmethodID getVirtualKeyQuietTimeMillis;
@@ -333,7 +332,6 @@ public:
bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent, bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override; uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override;
void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override; void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override;
bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, int32_t injectorUid) override;
void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override; void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
void setPointerCapture(const PointerCaptureRequest& request) override; void setPointerCapture(const PointerCaptureRequest& request) override;
void notifyDropWindow(const sp<IBinder>& token, float x, float y) override; void notifyDropWindow(const sp<IBinder>& token, float x, float y) override;
@@ -1380,19 +1378,6 @@ void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType,
android_server_PowerManagerService_userActivity(eventTime, eventType, displayId); android_server_PowerManagerService_userActivity(eventTime, eventType, displayId);
} }
bool NativeInputManager::checkInjectEventsPermissionNonReentrant(int32_t injectorPid,
int32_t injectorUid) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
jboolean result =
env->CallBooleanMethod(mServiceObj, gServiceClassInfo.checkInjectEventsPermission,
injectorPid, injectorUid);
if (checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission")) {
result = false;
}
return result;
}
void NativeInputManager::onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) { void NativeInputManager::onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) {
ATRACE_CALL(); ATRACE_CALL();
JNIEnv* env = jniEnv(); JNIEnv* env = jniEnv();
@@ -1709,10 +1694,11 @@ static void nativeSetBlockUntrustedTouchesMode(JNIEnv* env, jobject nativeImplOb
} }
static jint nativeInjectInputEvent(JNIEnv* env, jobject nativeImplObj, jobject inputEventObj, static jint nativeInjectInputEvent(JNIEnv* env, jobject nativeImplObj, jobject inputEventObj,
jint injectorPid, jint injectorUid, jint syncMode, jboolean injectIntoUid, jint uid, jint syncMode,
jint timeoutMillis, jint policyFlags) { jint timeoutMillis, jint policyFlags) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj); NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
const std::optional<int32_t> targetUid = injectIntoUid ? std::make_optional(uid) : std::nullopt;
// static_cast is safe because the value was already checked at the Java layer // static_cast is safe because the value was already checked at the Java layer
InputEventInjectionSync mode = static_cast<InputEventInjectionSync>(syncMode); InputEventInjectionSync mode = static_cast<InputEventInjectionSync>(syncMode);
@@ -1725,8 +1711,7 @@ static jint nativeInjectInputEvent(JNIEnv* env, jobject nativeImplObj, jobject i
} }
const InputEventInjectionResult result = const InputEventInjectionResult result =
im->getInputManager()->getDispatcher().injectInputEvent(&keyEvent, injectorPid, im->getInputManager()->getDispatcher().injectInputEvent(&keyEvent, targetUid, mode,
injectorUid, mode,
std::chrono::milliseconds( std::chrono::milliseconds(
timeoutMillis), timeoutMillis),
uint32_t(policyFlags)); uint32_t(policyFlags));
@@ -1739,8 +1724,8 @@ static jint nativeInjectInputEvent(JNIEnv* env, jobject nativeImplObj, jobject i
} }
const InputEventInjectionResult result = const InputEventInjectionResult result =
im->getInputManager()->getDispatcher().injectInputEvent(motionEvent, injectorPid, im->getInputManager()->getDispatcher().injectInputEvent(motionEvent, targetUid,
injectorUid, mode, mode,
std::chrono::milliseconds( std::chrono::milliseconds(
timeoutMillis), timeoutMillis),
uint32_t(policyFlags)); uint32_t(policyFlags));
@@ -2345,7 +2330,7 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"setMaximumObscuringOpacityForTouch", "(F)V", {"setMaximumObscuringOpacityForTouch", "(F)V",
(void*)nativeSetMaximumObscuringOpacityForTouch}, (void*)nativeSetMaximumObscuringOpacityForTouch},
{"setBlockUntrustedTouchesMode", "(I)V", (void*)nativeSetBlockUntrustedTouchesMode}, {"setBlockUntrustedTouchesMode", "(I)V", (void*)nativeSetBlockUntrustedTouchesMode},
{"injectInputEvent", "(Landroid/view/InputEvent;IIIII)I", (void*)nativeInjectInputEvent}, {"injectInputEvent", "(Landroid/view/InputEvent;ZIIII)I", (void*)nativeInjectInputEvent},
{"verifyInputEvent", "(Landroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;", {"verifyInputEvent", "(Landroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;",
(void*)nativeVerifyInputEvent}, (void*)nativeVerifyInputEvent},
{"toggleCapsLock", "(I)V", (void*)nativeToggleCapsLock}, {"toggleCapsLock", "(I)V", (void*)nativeToggleCapsLock},
@@ -2492,9 +2477,6 @@ int register_android_server_InputManager(JNIEnv* env) {
"dispatchUnhandledKey", "dispatchUnhandledKey",
"(Landroid/os/IBinder;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;"); "(Landroid/os/IBinder;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;");
GET_METHOD_ID(gServiceClassInfo.checkInjectEventsPermission, clazz,
"checkInjectEventsPermission", "(II)Z");
GET_METHOD_ID(gServiceClassInfo.onPointerDisplayIdChanged, clazz, "onPointerDisplayIdChanged", GET_METHOD_ID(gServiceClassInfo.onPointerDisplayIdChanged, clazz, "onPointerDisplayIdChanged",
"(IFF)V"); "(IFF)V");