Add InputDispatcher unit tests for untrusted touches [1/n]

Start off with the simple cases.

Test: atest inputflinger_tests
Bug: 158002302
Bug: 177840583
Change-Id: I5b63dd5089a579e7a1d537a1825c1df640d094c9
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 01945db..79a6774 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -31,6 +31,7 @@
 using android::base::StringPrintf;
 using android::os::InputEventInjectionResult;
 using android::os::InputEventInjectionSync;
+using android::os::TouchOcclusionMode;
 using namespace android::flag_operators;
 
 namespace android::inputdispatcher {
@@ -727,8 +728,9 @@
         ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
     }
 
-    void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
-                      int32_t expectedFlags) {
+    void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
+                      std::optional<int32_t> expectedDisplayId,
+                      std::optional<int32_t> expectedFlags) {
         InputEvent* event = consume();
 
         ASSERT_NE(nullptr, event) << mName.c_str()
@@ -737,19 +739,25 @@
                 << mName.c_str() << " expected " << inputEventTypeToString(expectedEventType)
                 << " event, got " << inputEventTypeToString(event->getType()) << " event";
 
-        EXPECT_EQ(expectedDisplayId, event->getDisplayId());
+        if (expectedDisplayId.has_value()) {
+            EXPECT_EQ(expectedDisplayId, event->getDisplayId());
+        }
 
         switch (expectedEventType) {
             case AINPUT_EVENT_TYPE_KEY: {
                 const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event);
                 EXPECT_EQ(expectedAction, keyEvent.getAction());
-                EXPECT_EQ(expectedFlags, keyEvent.getFlags());
+                if (expectedFlags.has_value()) {
+                    EXPECT_EQ(expectedFlags.value(), keyEvent.getFlags());
+                }
                 break;
             }
             case AINPUT_EVENT_TYPE_MOTION: {
                 const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
                 EXPECT_EQ(expectedAction, motionEvent.getAction());
-                EXPECT_EQ(expectedFlags, motionEvent.getFlags());
+                if (expectedFlags.has_value()) {
+                    EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags());
+                }
                 break;
             }
             case AINPUT_EVENT_TYPE_FOCUS: {
@@ -854,6 +862,7 @@
         mInfo.name = name;
         mInfo.type = InputWindowInfo::Type::APPLICATION;
         mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+        mInfo.alpha = 1.0;
         mInfo.frameLeft = 0;
         mInfo.frameTop = 0;
         mInfo.frameRight = WIDTH;
@@ -883,6 +892,12 @@
 
     void setPaused(bool paused) { mInfo.paused = paused; }
 
+    void setAlpha(float alpha) { mInfo.alpha = alpha; }
+
+    void setTouchOcclusionMode(android::os::TouchOcclusionMode mode) {
+        mInfo.touchOcclusionMode = mode;
+    }
+
     void setFrame(const Rect& frame) {
         mInfo.frameLeft = frame.left;
         mInfo.frameTop = frame.top;
@@ -928,6 +943,11 @@
 
     void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
                            int32_t expectedFlags = 0) {
+        consumeAnyMotionDown(expectedDisplayId, expectedFlags);
+    }
+
+    void consumeAnyMotionDown(std::optional<int32_t> expectedDisplayId = std::nullopt,
+                              std::optional<int32_t> expectedFlags = std::nullopt) {
         consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
                      expectedFlags);
     }
@@ -971,8 +991,9 @@
         mInputReceiver->consumeCaptureEvent(hasCapture);
     }
 
-    void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
-                      int32_t expectedFlags) {
+    void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
+                      std::optional<int32_t> expectedDisplayId,
+                      std::optional<int32_t> expectedFlags) {
         ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver";
         mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId,
                                      expectedFlags);
@@ -4240,4 +4261,112 @@
     mSecondWindow->consumeCaptureEvent(true);
 }
 
+class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest {
+protected:
+    constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8;
+    static const int32_t APP_A_UID = 10001;
+    static const int32_t APP_B_UID = 10002;
+
+    sp<FakeWindowHandle> mTouchWindow;
+
+    virtual void SetUp() override {
+        InputDispatcherTest::SetUp();
+        mTouchWindow = getWindow(APP_A_UID, "Touched");
+        mDispatcher->setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode::BLOCK);
+        mDispatcher->setMaximumObscuringOpacityForTouch(MAXIMUM_OBSCURING_OPACITY);
+    }
+
+    virtual void TearDown() override {
+        InputDispatcherTest::TearDown();
+        mTouchWindow.clear();
+    }
+
+    sp<FakeWindowHandle> getOccludingWindow(int32_t uid, std::string name,
+                                            os::TouchOcclusionMode mode) {
+        sp<FakeWindowHandle> window = getWindow(uid, name);
+        window->setFlags(InputWindowInfo::Flag::NOT_TOUCHABLE);
+        window->setTouchOcclusionMode(mode);
+        return window;
+    }
+
+    sp<FakeWindowHandle> getWindow(int32_t uid, std::string name) {
+        std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
+        sp<FakeWindowHandle> window =
+                new FakeWindowHandle(app, mDispatcher, name, ADISPLAY_ID_DEFAULT);
+        // Generate an arbitrary PID based on the UID
+        window->setOwnerInfo(1777 + (uid % 10000), uid);
+        return window;
+    }
+
+    void touch(const std::vector<PointF>& points = {PointF{100, 200}}) {
+        NotifyMotionArgs args =
+                generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                   ADISPLAY_ID_DEFAULT, points);
+        mDispatcher->notifyMotion(&args);
+    }
+};
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithBlockUntrustedOcclusionMode_BlocksTouch) {
+    const sp<FakeWindowHandle>& window =
+            getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});
+
+    touch();
+
+    mTouchWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithAllowOcclusionMode_AllowsTouch) {
+    const sp<FakeWindowHandle>& window =
+            getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::ALLOW);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});
+
+    touch();
+
+    mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, TouchOutsideOccludingWindow_AllowsTouch) {
+    const sp<FakeWindowHandle>& window =
+            getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
+    window->setFrame(Rect(0, 0, 50, 50));
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});
+
+    touch({PointF{100, 100}});
+
+    mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowFromSameUid_AllowsTouch) {
+    const sp<FakeWindowHandle>& window =
+            getOccludingWindow(APP_A_UID, "A", TouchOcclusionMode::BLOCK_UNTRUSTED);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});
+
+    touch();
+
+    mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityBelowThreshold_AllowsTouch) {
+    const sp<FakeWindowHandle>& window =
+            getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY);
+    window->setAlpha(0.7);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});
+
+    touch();
+
+    mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityAboveThreshold_BlocksTouch) {
+    const sp<FakeWindowHandle>& window =
+            getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY);
+    window->setAlpha(0.9);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, mTouchWindow}}});
+
+    touch();
+
+    mTouchWindow->assertNoEvents();
+}
+
 } // namespace android::inputdispatcher