diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index eb4585dd7097c..a74438ae8452d 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -376,14 +376,16 @@ public class Instrumentation { Debug.stopMethodTracing(); } } - + /** - * Force the global system in or out of touch mode. This can be used if - * your instrumentation relies on the UI being in one more or the other - * when it starts. - * - * @param inTouch Set to true to be in touch mode, false to be in - * focus mode. + * Force the global system in or out of touch mode. This can be used if your + * instrumentation relies on the UI being in one more or the other when it starts. + * + *

Note: Starting from Android {@link Build.VERSION_CODES#TIRAMISU}, this method + * will only have an effect if the calling process is also the focused window owner or has + * {@link android.permission#MODIFY_TOUCH_MODE_STATE} permission granted. + * + * @param inTouch Set to true to be in touch mode, false to be in focus mode. */ public void setInTouchMode(boolean inTouch) { try { @@ -393,11 +395,11 @@ public class Instrumentation { // Shouldn't happen! } } - + /** * Schedule a callback for when the application's main thread goes idle * (has no more events to process). - * + * * @param recipient Called the next time the thread's message queue is * idle. */ diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index f83431b58c625..004a2eb749ebf 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -39,6 +39,7 @@ + diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 64b4da7c5bba2..52db5c73721c0 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -307,7 +307,8 @@ public class InputManagerService extends IInputManager.Stub private static native void nativeRemoveInputChannel(long ptr, IBinder connectionToken); private static native void nativePilferPointers(long ptr, IBinder token); private static native void nativeSetInputFilterEnabled(long ptr, boolean enable); - private static native void nativeSetInTouchMode(long ptr, boolean inTouchMode); + private static native boolean nativeSetInTouchMode(long ptr, boolean inTouchMode, int pid, + int uid, boolean hasPermission); private static native void nativeSetMaximumObscuringOpacityForTouch(long ptr, float opacity); private static native void nativeSetBlockUntrustedTouchesMode(long ptr, int mode); private static native int nativeInjectInputEvent(long ptr, InputEvent event, @@ -872,12 +873,16 @@ public class InputManagerService extends IInputManager.Stub * other apps, when they become focused. * * When input dispatches focus to the apps, the touch mode state - * will be sent together with the focus change. + * will be sent together with the focus change (but each one in its own event). * - * @param inTouchMode true if the device is in touch mode. + * @param inTouchMode true if the device is in touch mode + * @param pid the pid of the process that requested to switch touch mode state + * @param uid the uid of the process that requested to switch touch mode state + * @param hasPermission if set to {@code true} then no further authorization will be performed + * @return {@code true} if the touch mode was successfully changed, {@code false} otherwise */ - public void setInTouchMode(boolean inTouchMode) { - nativeSetInTouchMode(mPtr, inTouchMode); + public boolean setInTouchMode(boolean inTouchMode, int pid, int uid, boolean hasPermission) { + return nativeSetInTouchMode(mPtr, inTouchMode, pid, uid, hasPermission); } @Override // Binder call diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 1167cb531c761..9fb07fbe18ecf 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.INPUT_CONSUMER; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS; import static android.Manifest.permission.MANAGE_APP_TOKENS; +import static android.Manifest.permission.MODIFY_TOUCH_MODE_STATE; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; import static android.Manifest.permission.RESTRICTED_VR_ACCESS; @@ -38,6 +39,7 @@ import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE; import static android.os.Process.SYSTEM_UID; import static android.os.Process.myPid; +import static android.os.Process.myUid; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW; @@ -1202,7 +1204,8 @@ public class WindowManagerService extends IWindowManager.Stub com.android.internal.R.bool.config_hasPermanentDpad); mInTouchMode = context.getResources().getBoolean( com.android.internal.R.bool.config_defaultInTouchMode); - inputManager.setInTouchMode(mInTouchMode); + inputManager.setInTouchMode( + mInTouchMode, myPid(), myUid(), /* hasPermission = */ true); mDrawLockTimeoutMillis = context.getResources().getInteger( com.android.internal.R.integer.config_drawLockTimeoutMillis); mAllowAnimationsInLowPowerMode = context.getResources().getBoolean( @@ -2669,7 +2672,6 @@ public class WindowManagerService extends IWindowManager.Stub } boolean checkCallingPermission(String permission, String func, boolean printLog) { - // Quick check: if the calling permission is me, it's all okay. if (Binder.getCallingPid() == myPid()) { return true; } @@ -3703,12 +3705,33 @@ public class WindowManagerService extends IWindowManager.Stub } } - @Override + /** + * Sets the touch mode state. + * + * To be able to change touch mode state, the caller must either own the focused window, or must + * have the MODIFY_TOUCH_MODE_STATE permission. + * + * @param mode the touch mode to set + */ + @Override // Binder call public void setInTouchMode(boolean mode) { synchronized (mGlobalLock) { - mInTouchMode = mode; + if (mInTouchMode == mode) { + return; + } + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final boolean hasPermission = checkCallingPermission(MODIFY_TOUCH_MODE_STATE, + "setInTouchMode()"); + final long token = Binder.clearCallingIdentity(); + try { + if (mInputManager.setInTouchMode(mode, pid, uid, hasPermission)) { + mInTouchMode = mode; + } + } finally { + Binder.restoreCallingIdentity(token); + } } - mInputManager.setInTouchMode(mode); } boolean getInTouchMode() { diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 8cb27e179c19e..330bdebbd74d6 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -1707,7 +1707,6 @@ static void nativePilferPointers(JNIEnv* env, jclass /* clazz */, jlong ptr, job im->pilferPointers(token); } - static void nativeSetInputFilterEnabled(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jboolean enabled) { NativeInputManager* im = reinterpret_cast(ptr); @@ -1715,11 +1714,13 @@ static void nativeSetInputFilterEnabled(JNIEnv* /* env */, jclass /* clazz */, im->getInputManager()->getDispatcher().setInputFilterEnabled(enabled); } -static void nativeSetInTouchMode(JNIEnv* /* env */, jclass /* clazz */, - jlong ptr, jboolean inTouchMode) { +static jboolean nativeSetInTouchMode(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, + jboolean inTouchMode, jint pid, jint uid, + jboolean hasPermission) { NativeInputManager* im = reinterpret_cast(ptr); - im->getInputManager()->getDispatcher().setInTouchMode(inTouchMode); + return im->getInputManager()->getDispatcher().setInTouchMode(inTouchMode, pid, uid, + hasPermission); } static void nativeSetMaximumObscuringOpacityForTouch(JNIEnv* /* env */, jclass /* clazz */, @@ -2386,7 +2387,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"nativeRemoveInputChannel", "(JLandroid/os/IBinder;)V", (void*)nativeRemoveInputChannel}, {"nativePilferPointers", "(JLandroid/os/IBinder;)V", (void*)nativePilferPointers}, {"nativeSetInputFilterEnabled", "(JZ)V", (void*)nativeSetInputFilterEnabled}, - {"nativeSetInTouchMode", "(JZ)V", (void*)nativeSetInTouchMode}, + {"nativeSetInTouchMode", "(JZIIZ)Z", (void*)nativeSetInTouchMode}, {"nativeSetMaximumObscuringOpacityForTouch", "(JF)V", (void*)nativeSetMaximumObscuringOpacityForTouch}, {"nativeSetBlockUntrustedTouchesMode", "(JI)V", (void*)nativeSetBlockUntrustedTouchesMode},