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 776a5117cb2ec..c9bd3710ca79b 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 354d1838709df..94c694d39df74 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -305,7 +305,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,
@@ -866,12 +867,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 039359bd6b50d..c4907c2d99037 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;
@@ -1186,7 +1188,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(
@@ -2652,7 +2655,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;
}
@@ -3692,12 +3694,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 e5529f1997a38..3c122b03d0d17 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1704,7 +1704,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);
@@ -1712,11 +1711,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 */,
@@ -2383,7 +2384,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},