Merge "Change input injection security model to require INJECT_EVENTS permission" into tm-dev am: 0df8e9cedd

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17125748

Change-Id: Ic2f3285cf46bdab97f9a8cdf4b1e76a08709794f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Prabir Pradhan
2022-04-05 11:08:54 +00:00
committed by Automerger Merge Worker
9 changed files with 195 additions and 115 deletions

View File

@@ -562,14 +562,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_RESULT); event, InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT, Process.myUid());
if (syncAfter) { if (syncAfter) {
WindowManagerGlobal.getWindowManagerService() WindowManagerGlobal.getWindowManagerService()
@@ -1182,19 +1193,26 @@ 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_POINTER)) {
throw new IllegalArgumentException(
"Cannot inject pointer events from sendTrackballEventSync()."
+ " Use sendPointerSync() to inject pointer events.");
}
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,10 +57,11 @@ 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 permission.
// applications, 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.
@UnsupportedAppUsage @UnsupportedAppUsage
boolean injectInputEvent(in InputEvent ev, int mode); boolean injectInputEvent(in InputEvent ev, int mode, int targetUid);
VerifiedInputEvent verifyInputEvent(in InputEvent ev); VerifiedInputEvent verifyInputEvent(in InputEvent ev);

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.injectInputEvent(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

@@ -17215,17 +17215,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

@@ -2367,7 +2367,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 {
@@ -6894,7 +6895,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;
@@ -325,8 +326,7 @@ public class InputManagerService extends IInputManager.Stub
private static native void nativeSetMaximumObscuringOpacityForTouch(long ptr, float opacity); private static native void nativeSetMaximumObscuringOpacityForTouch(long ptr, float opacity);
private static native void nativeSetBlockUntrustedTouchesMode(long ptr, int mode); private static native void nativeSetBlockUntrustedTouchesMode(long ptr, int mode);
private static native int nativeInjectInputEvent(long ptr, InputEvent event, private static native int nativeInjectInputEvent(long ptr, InputEvent event,
int injectorPid, int injectorUid, int syncMode, int timeoutMillis, boolean injectIntoUid, int uid, int syncMode, int timeoutMillis, int policyFlags);
int policyFlags);
private static native VerifiedInputEvent nativeVerifyInputEvent(long ptr, InputEvent event); private static native VerifiedInputEvent nativeVerifyInputEvent(long ptr, InputEvent event);
private static native void nativeToggleCapsLock(long ptr, int deviceId); private static native void nativeToggleCapsLock(long ptr, int deviceId);
private static native void nativeDisplayRemoved(long ptr, int displayId); private static native void nativeDisplayRemoved(long ptr, int displayId);
@@ -897,7 +897,15 @@ 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, 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
@@ -906,22 +914,41 @@ 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 = nativeInjectInputEvent(mPtr, event, pid, uid, mode, result = nativeInjectInputEvent(mPtr, 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.");
}
// Attempt to inject into a window owned by the instrumentation source of the caller
// because it is possible that tests adopt the identity of the shell when launching
// activities that they would like to inject into.
final ActivityManagerInternal ami =
LocalServices.getService(ActivityManagerInternal.class);
Objects.requireNonNull(ami, "ActivityManagerInternal should not be null.");
final int instrUid = ami.getInstrumentationSourceUid(Binder.getCallingUid());
if (instrUid != Process.INVALID_UID && targetUid != instrUid) {
Slog.w(TAG, "Targeted input event was not directed at a window owned by uid "
+ targetUid + ". Attempting to inject into window owned by "
+ "instrumentation source uid " + instrUid + ".");
return injectInputEvent(event, mode, instrUid);
}
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;
@@ -2757,8 +2784,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;
@@ -2767,6 +2798,18 @@ 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 && mContext.checkPermission(permission,
-1 /*pid*/, instrumentationUid) == PackageManager.PERMISSION_GRANTED) {
return true;
}
}
String msg = "Permission Denial: " + func + " from pid=" String msg = "Permission Denial: " + func + " from pid="
+ Binder.getCallingPid() + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid() + ", uid=" + Binder.getCallingUid()
@@ -3010,13 +3053,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) {
@@ -3459,12 +3495,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) {
nativeInjectInputEvent(mPtr, event, 0, 0, nativeInjectInputEvent(mPtr, 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

@@ -183,7 +183,8 @@ public final class LogcatManagerService extends SystemService {
ActivityManagerInternal ami = LocalServices.getService( ActivityManagerInternal ami = LocalServices.getService(
ActivityManagerInternal.class); ActivityManagerInternal.class);
boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(mUid); boolean isCallerInstrumented =
ami.getInstrumentationSourceUid(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 (isCallerInstrumented) { if (isCallerInstrumented) {

View File

@@ -106,7 +106,6 @@ static struct {
jmethodID interceptMotionBeforeQueueingNonInteractive; jmethodID interceptMotionBeforeQueueingNonInteractive;
jmethodID interceptKeyBeforeDispatching; jmethodID interceptKeyBeforeDispatching;
jmethodID dispatchUnhandledKey; jmethodID dispatchUnhandledKey;
jmethodID checkInjectEventsPermission;
jmethodID onPointerDownOutsideFocus; jmethodID onPointerDownOutsideFocus;
jmethodID getVirtualKeyQuietTimeMillis; jmethodID getVirtualKeyQuietTimeMillis;
jmethodID getExcludedDeviceNames; jmethodID getExcludedDeviceNames;
@@ -329,7 +328,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;
@@ -1368,18 +1366,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();
@@ -1706,11 +1692,12 @@ static void nativeSetBlockUntrustedTouchesMode(JNIEnv* env, jclass /* clazz */,
static_cast<BlockUntrustedTouchesMode>(mode)); static_cast<BlockUntrustedTouchesMode>(mode));
} }
static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */, static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */, jlong ptr,
jlong ptr, jobject inputEventObj, jint injectorPid, jint injectorUid, jobject inputEventObj, jboolean injectIntoUid, jint uid,
jint syncMode, jint timeoutMillis, jint policyFlags) { jint syncMode, jint timeoutMillis, jint policyFlags) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
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);
@@ -1723,8 +1710,7 @@ static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */,
} }
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));
@@ -1737,8 +1723,8 @@ static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */,
} }
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));
@@ -2359,7 +2345,7 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"nativeSetMaximumObscuringOpacityForTouch", "(JF)V", {"nativeSetMaximumObscuringOpacityForTouch", "(JF)V",
(void*)nativeSetMaximumObscuringOpacityForTouch}, (void*)nativeSetMaximumObscuringOpacityForTouch},
{"nativeSetBlockUntrustedTouchesMode", "(JI)V", (void*)nativeSetBlockUntrustedTouchesMode}, {"nativeSetBlockUntrustedTouchesMode", "(JI)V", (void*)nativeSetBlockUntrustedTouchesMode},
{"nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIII)I", {"nativeInjectInputEvent", "(JLandroid/view/InputEvent;ZIIII)I",
(void*)nativeInjectInputEvent}, (void*)nativeInjectInputEvent},
{"nativeVerifyInputEvent", "(JLandroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;", {"nativeVerifyInputEvent", "(JLandroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;",
(void*)nativeVerifyInputEvent}, (void*)nativeVerifyInputEvent},
@@ -2499,9 +2485,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.onPointerDownOutsideFocus, clazz, GET_METHOD_ID(gServiceClassInfo.onPointerDownOutsideFocus, clazz,
"onPointerDownOutsideFocus", "(Landroid/os/IBinder;)V"); "onPointerDownOutsideFocus", "(Landroid/os/IBinder;)V");