InputDispatcher: Allow spy window to receive entire gesture after pilfer

After a spy window pilfers pointers, it should be albe to receive any
new pointers within its touchable bounds.

Bug: 217376964
Test: atest inputflinger_tests
Change-Id: Iab8360f2f8e3db978cf6c68f19a538b28f7dfac1
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index a1ac229..dc46f30 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1987,7 +1987,7 @@
     TouchState tempTouchState;
     if (const auto it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
         oldState = &(it->second);
-        tempTouchState.copyFrom(*oldState);
+        tempTouchState = *oldState;
     }
 
     bool isSplit = tempTouchState.split;
@@ -5510,9 +5510,9 @@
     ALOGI("Channel %s is stealing touch from %s", requestingChannel->getName().c_str(),
           canceledWindows.c_str());
 
-    // Then clear the current touch state so we stop dispatching to them as well.
-    state.split = false;
+    // Prevent the gesture from being sent to any other windows.
     state.filterWindowsExcept(token);
+    state.preventNewTargets = true;
     return OK;
 }
 
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index ab86196..b63fe10 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -25,27 +25,8 @@
 
 namespace android::inputdispatcher {
 
-TouchState::TouchState()
-      : down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) {}
-
-TouchState::~TouchState() {}
-
 void TouchState::reset() {
-    down = false;
-    split = false;
-    deviceId = -1;
-    source = 0;
-    displayId = ADISPLAY_ID_NONE;
-    windows.clear();
-}
-
-void TouchState::copyFrom(const TouchState& other) {
-    down = other.down;
-    split = other.split;
-    deviceId = other.deviceId;
-    source = other.source;
-    displayId = other.displayId;
-    windows = other.windows;
+    *this = TouchState();
 }
 
 void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int32_t targetFlags,
@@ -66,6 +47,8 @@
         }
     }
 
+    if (preventNewTargets) return; // Don't add new TouchedWindows.
+
     TouchedWindow touchedWindow;
     touchedWindow.windowHandle = windowHandle;
     touchedWindow.targetFlags = targetFlags;
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index e154ed3..9efb280 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -29,17 +29,24 @@
 namespace inputdispatcher {
 
 struct TouchState {
-    bool down;
-    bool split;
-    int32_t deviceId;  // id of the device that is currently down, others are rejected
-    uint32_t source;   // source of the device that is current down, others are rejected
-    int32_t displayId; // id to the display that currently has a touch, others are rejected
+    bool down = false;
+    bool split = false;
+    bool preventNewTargets = false;
+
+    // id of the device that is currently down, others are rejected
+    int32_t deviceId = -1;
+    // source of the device that is current down, others are rejected
+    uint32_t source = 0;
+    // id to the display that currently has a touch, others are rejected
+    int32_t displayId = ADISPLAY_ID_NONE;
+
     std::vector<TouchedWindow> windows;
 
-    TouchState();
-    ~TouchState();
+    TouchState() = default;
+    ~TouchState() = default;
+    TouchState& operator=(const TouchState&) = default;
+
     void reset();
-    void copyFrom(const TouchState& other);
     void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
                            int32_t targetFlags, BitSet32 pointerIds);
     void removeWindowByToken(const sp<IBinder>& token);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 612fb84..ef1ee26 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -6511,32 +6511,29 @@
 }
 
 /**
- * After a spy window pilfers pointers, new pointers that go down should not go to any foreground
- * windows.
+ * After a spy window pilfers pointers, new pointers that go down in its bounds should be sent to
+ * the spy, but not to any other windows.
  */
-TEST_F(InputDispatcherSpyWindowTest, NoSplitAfterPilfer) {
-    // Create a touch modal spy that spies on the entire display.
-    auto spy = createSpy(static_cast<WindowInfo::Flag>(0));
-
-    // Create a non touch modal window that supports split touch.
+TEST_F(InputDispatcherSpyWindowTest, ContinuesToReceiveGestureAfterPilfer) {
+    auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
     auto window = createForeground();
-    window->setFrame(Rect(0, 0, 100, 100));
     window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
 
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
 
-    // First finger down, no window touched.
+    // First finger down on the window and the spy.
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                {100, 200}))
             << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
-    spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
-    window->assertNoEvents();
+    spy->consumeMotionDown();
+    window->consumeMotionDown();
 
-    // Spy window pilfer the pointers.
+    // Spy window pilfers the pointers.
     EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
+    window->consumeMotionCancel();
 
-    // Second finger down on window, the window should not receive touch down.
+    // Second finger down on the window and spy, but the window should not receive the pointer down.
     const MotionEvent secondFingerDownEvent =
             MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
                                        (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
@@ -6553,10 +6550,28 @@
                                 InputEventInjectionSync::WAIT_FOR_RESULT))
             << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
 
+    spy->consumeMotionPointerDown(1 /*pointerIndex*/);
+
+    // Third finger goes down outside all windows, so injection should fail.
+    const MotionEvent thirdFingerDownEvent =
+            MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                       (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                               AINPUT_SOURCE_TOUCHSCREEN)
+                    .displayId(ADISPLAY_ID_DEFAULT)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+                                     .x(100)
+                                     .y(200))
+                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+                    .pointer(PointerBuilder(/* id */ 2, AMOTION_EVENT_TOOL_TYPE_FINGER).x(-5).y(-5))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::FAILED,
+              injectMotionEvent(mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    spy->assertNoEvents();
     window->assertNoEvents();
-    // Since we no longer allow splitting, the spy will not receive new pointers.
-    // TODO(b/217376964): Add a way for the pilfering window to receive the rest of the gesture.
-    spy->consumeMotionMove();
 }
 
 /**