(TouchMode Permission 2.1/n) Add permission check when switching touch mode

This CL adds the following permission checks when switching touch mode state:
1. Allow touch mode switch if caller is granted with MODIFY_TOUCH_MODE_STATE;
2. If caller is not granted with MODIFY_TOUCH_MODE_STATE, touch mode
state change will be allowed if the caller's uid matches the focused
window owner's uid.

Touch mode switch will be denied if none of the conditions above match.

Bug: 198487159
Test: atest inputflinger_tests
Test: atest libinput_tests

Change-Id: Ic704682ac41fe470207d9be132b4c0f1a8019f39
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index d1982fc..5448e04 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -137,7 +137,7 @@
 // Number of recent events to keep for debugging purposes.
 constexpr size_t RECENT_QUEUE_MAX_SIZE = 10;
 
-// Event log tags. See EventLogTags.logtags for reference
+// Event log tags. See EventLogTags.logtags for reference.
 constexpr int LOGTAG_INPUT_INTERACTION = 62000;
 constexpr int LOGTAG_INPUT_FOCUS = 62001;
 constexpr int LOGTAG_INPUT_CANCEL = 62003;
@@ -4950,23 +4950,42 @@
     mLooper->wake();
 }
 
-void InputDispatcher::setInTouchMode(bool inTouchMode) {
+bool InputDispatcher::setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid,
+                                     bool hasPermission) {
     bool needWake = false;
     {
         std::scoped_lock lock(mLock);
         if (mInTouchMode == inTouchMode) {
-            return;
+            return false;
         }
         if (DEBUG_TOUCH_MODE) {
-            ALOGD("Request to change touch mode from %s to %s", toString(mInTouchMode),
-                  toString(inTouchMode));
-            // TODO(b/198487159): Also print the current last interacted apps.
+            ALOGD("Request to change touch mode from %s to %s (calling pid=%d, uid=%d, "
+                  "hasPermission=%s)",
+                  toString(mInTouchMode), toString(inTouchMode), pid, uid, toString(hasPermission));
+        }
+        if (!hasPermission) {
+            const sp<IBinder> focusedToken =
+                    mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
+
+            //  TODO(b/198487159): if no window is currently focused, then we need to check the last
+            //      interacted window (within 1 second timeout). We should allow touch mode change
+            //      if the last interacted window owner's pid/uid match the calling ones.
+            if (focusedToken == nullptr) {
+                return false;
+            }
+            const sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(focusedToken);
+            if (windowHandle == nullptr) {
+                return false;
+            }
+            const WindowInfo* windowInfo = windowHandle->getInfo();
+            if (pid != windowInfo->ownerPid || uid != windowInfo->ownerUid) {
+                return false;
+            }
         }
 
         // TODO(b/198499018): Store touch mode per display.
         mInTouchMode = inTouchMode;
 
-        // TODO(b/198487159): Enforce that only last interacted apps can change touch mode.
         auto entry = std::make_unique<TouchModeEntry>(mIdGenerator.nextId(), now(), inTouchMode);
         needWake = enqueueInboundEventLocked(std::move(entry));
     } // release lock
@@ -4974,6 +4993,7 @@
     if (needWake) {
         mLooper->wake();
     }
+    return true;
 }
 
 void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 7564839..68950cb 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -117,7 +117,7 @@
     void setFocusedDisplay(int32_t displayId) override;
     void setInputDispatchMode(bool enabled, bool frozen) override;
     void setInputFilterEnabled(bool enabled) override;
-    void setInTouchMode(bool inTouchMode) override;
+    bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) override;
     void setMaximumObscuringOpacityForTouch(float opacity) override;
     void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
 
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 16a6f16..05b5236 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -123,8 +123,10 @@
      * Touch mode is a global state that apps may enter / exit based on specific
      * user interactions with input devices.
      * If true, the device is in touch mode.
+     *
+     * Returns true when changing touch mode state.
      */
-    virtual void setInTouchMode(bool inTouchMode) = 0;
+    virtual bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) = 0;
 
     /**
      * Sets the maximum allowed obscuring opacity by UID to propagate touches.
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index aa2f832..497a6b9 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -3112,6 +3112,7 @@
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+    const WindowInfo& windowInfo = *window->getInfo();
 
     // Set focused application.
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -3128,7 +3129,8 @@
     window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/);
 
     SCOPED_TRACE("Disable touch mode");
-    mDispatcher->setInTouchMode(false);
+    mDispatcher->setInTouchMode(false, windowInfo.ownerPid, windowInfo.ownerUid,
+                                /* hasPermission */ true);
     window->consumeTouchModeEvent(false);
     window->setFocusable(true);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -3141,7 +3143,8 @@
     window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/);
 
     SCOPED_TRACE("Enable touch mode again");
-    mDispatcher->setInTouchMode(true);
+    mDispatcher->setInTouchMode(true, windowInfo.ownerPid, windowInfo.ownerUid,
+                                /* hasPermission */ true);
     window->consumeTouchModeEvent(true);
     window->setFocusable(true);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -6300,19 +6303,23 @@
         mWindow->consumeFocusEvent(true);
     }
 
-    void changeAndVerifyTouchMode(bool inTouchMode) {
-        mDispatcher->setInTouchMode(inTouchMode);
+    void changeAndVerifyTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) {
+        mDispatcher->setInTouchMode(inTouchMode, pid, uid, hasPermission);
         mWindow->consumeTouchModeEvent(inTouchMode);
         mSecondWindow->consumeTouchModeEvent(inTouchMode);
     }
 };
 
 TEST_F(InputDispatcherTouchModeChangedTests, ChangeTouchModeOnFocusedWindow) {
-    changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode);
+    const WindowInfo& windowInfo = *mWindow->getInfo();
+    changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode, windowInfo.ownerPid,
+                             windowInfo.ownerUid, /* hasPermission */ false);
 }
 
 TEST_F(InputDispatcherTouchModeChangedTests, EventIsNotGeneratedIfNotChangingTouchMode) {
-    mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode);
+    const WindowInfo& windowInfo = *mWindow->getInfo();
+    mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, windowInfo.ownerPid,
+                                windowInfo.ownerUid, /* hasPermission */ true);
     mWindow->assertNoEvents();
     mSecondWindow->assertNoEvents();
 }
@@ -6712,4 +6719,7 @@
     window->assertNoEvents();
 }
 
+// TODO(b/198487159): Add permission tests for touch mode switch once the validation is put in
+//     place.
+
 } // namespace android::inputdispatcher