InputDispatcher: Implement stylus interceptor

A stylus interceptor is a window that receives all stylus events within
its touchable bounds, while letting all other events be dispatched to
windows behind it. This makes it possible for the framework to create a
stylus interceptor on top of another app to implement handwriting
recognition.

The feature has no effect when the window flag FLAG_NOT_TOUCHABLE is
not set.

The feature has no effect when the window is not a trusted overlay.

Test: atest inputflinger_tests
Change-Id: Id4833840aa3088d21333d3ea08beffbded4debbc
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ba70929..b2d0fb8 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1003,6 +1003,7 @@
         mInfo.ownerPid = INJECTOR_PID;
         mInfo.ownerUid = INJECTOR_UID;
         mInfo.displayId = displayId;
+        mInfo.trustedOverlay = false;
     }
 
     sp<FakeWindowHandle> clone(
@@ -1054,7 +1055,9 @@
 
     void setFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags = flags; }
 
-    void setInputFeatures(WindowInfo::Feature features) { mInfo.inputFeatures = features; }
+    void setInputFeatures(Flags<WindowInfo::Feature> features) { mInfo.inputFeatures = features; }
+
+    void setTrustedOverlay(bool trustedOverlay) { mInfo.trustedOverlay = trustedOverlay; }
 
     void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
         mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
@@ -6600,4 +6603,97 @@
     spyRight->consumeMotionDown();
 }
 
+class InputDispatcherStylusInterceptorTest : public InputDispatcherTest {
+public:
+    std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupStylusOverlayScenario() {
+        std::shared_ptr<FakeApplicationHandle> overlayApplication =
+                std::make_shared<FakeApplicationHandle>();
+        sp<FakeWindowHandle> overlay =
+                new FakeWindowHandle(overlayApplication, mDispatcher, "Stylus interceptor window",
+                                     ADISPLAY_ID_DEFAULT);
+        overlay->setFocusable(false);
+        overlay->setOwnerInfo(111, 111);
+        overlay->setFlags(WindowInfo::Flag::NOT_TOUCHABLE | WindowInfo::Flag::SPLIT_TOUCH);
+        overlay->setInputFeatures(WindowInfo::Feature::INTERCEPTS_STYLUS);
+        overlay->setTrustedOverlay(true);
+
+        std::shared_ptr<FakeApplicationHandle> application =
+                std::make_shared<FakeApplicationHandle>();
+        sp<FakeWindowHandle> window =
+                new FakeWindowHandle(application, mDispatcher, "Application window",
+                                     ADISPLAY_ID_DEFAULT);
+        window->setFocusable(true);
+        window->setOwnerInfo(222, 222);
+        window->setFlags(WindowInfo::Flag::SPLIT_TOUCH);
+
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+        setFocusedWindow(window);
+        window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+        return {std::move(overlay), std::move(window)};
+    }
+
+    void sendFingerEvent(int32_t action) {
+        NotifyMotionArgs motionArgs =
+                generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+                                   ADISPLAY_ID_DEFAULT, {PointF{20, 20}});
+        mDispatcher->notifyMotion(&motionArgs);
+    }
+
+    void sendStylusEvent(int32_t action) {
+        NotifyMotionArgs motionArgs =
+                generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+                                   ADISPLAY_ID_DEFAULT, {PointF{30, 40}});
+        motionArgs.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
+        mDispatcher->notifyMotion(&motionArgs);
+    }
+};
+
+TEST_F(InputDispatcherStylusInterceptorTest, UntrustedOverlay_AbortsDispatcher) {
+    auto [overlay, window] = setupStylusOverlayScenario();
+    overlay->setTrustedOverlay(false);
+    // Configuring an untrusted overlay as a stylus interceptor should cause Dispatcher to abort.
+    ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}),
+                 ".* not a trusted overlay");
+}
+
+TEST_F(InputDispatcherStylusInterceptorTest, ConsmesOnlyStylusEvents) {
+    auto [overlay, window] = setupStylusOverlayScenario();
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+
+    sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
+    overlay->consumeMotionDown();
+    sendStylusEvent(AMOTION_EVENT_ACTION_UP);
+    overlay->consumeMotionUp();
+
+    sendFingerEvent(AMOTION_EVENT_ACTION_DOWN);
+    window->consumeMotionDown();
+    sendFingerEvent(AMOTION_EVENT_ACTION_UP);
+    window->consumeMotionUp();
+
+    overlay->assertNoEvents();
+    window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherStylusInterceptorTest, SpyWindowStylusInterceptor) {
+    auto [overlay, window] = setupStylusOverlayScenario();
+    overlay->setInputFeatures(overlay->getInfo()->inputFeatures | WindowInfo::Feature::SPY);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}});
+
+    sendStylusEvent(AMOTION_EVENT_ACTION_DOWN);
+    overlay->consumeMotionDown();
+    window->consumeMotionDown();
+    sendStylusEvent(AMOTION_EVENT_ACTION_UP);
+    overlay->consumeMotionUp();
+    window->consumeMotionUp();
+
+    sendFingerEvent(AMOTION_EVENT_ACTION_DOWN);
+    window->consumeMotionDown();
+    sendFingerEvent(AMOTION_EVENT_ACTION_UP);
+    window->consumeMotionUp();
+
+    overlay->assertNoEvents();
+    window->assertNoEvents();
+}
+
 } // namespace android::inputdispatcher