Fix drag and drop failed while recording video

The 'transferTouchFocus' would check if the two windows are exist and
if they are coming from same display. But the mirror surface would
share the same token of the original one. That could cause
'getWindowHandle' return the first window that not belongs to the
display of the target window.

To fix this, we would check the touch state to see if the from window
is being touched, and check if the target window is also coming from
same display, then do the transfer touch focus.

Bug: 195380978
Test: atest inputflinger_tests
Test: atest CrossAppDragAndDropTests
Test: enable video recording, do drag and drop
Change-Id: I458dfb138d446656117828f1229fcc871e8b5e1d
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index c60a62f..bf25269 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4784,6 +4784,18 @@
     mBlockUntrustedTouchesMode = mode;
 }
 
+std::pair<TouchState*, TouchedWindow*> InputDispatcher::findTouchStateAndWindowLocked(
+        const sp<IBinder>& token) {
+    for (auto& [displayId, state] : mTouchStatesByDisplay) {
+        for (TouchedWindow& w : state.windows) {
+            if (w.windowHandle->getToken() == token) {
+                return std::make_pair(&state, &w);
+            }
+        }
+    }
+    return std::make_pair(nullptr, nullptr);
+}
+
 bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
                                          bool isDragDrop) {
     if (fromToken == toToken) {
@@ -4796,58 +4808,43 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        sp<WindowInfoHandle> fromWindowHandle = getWindowHandleLocked(fromToken);
-        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken);
-        if (fromWindowHandle == nullptr || toWindowHandle == nullptr) {
-            ALOGW("Cannot transfer focus because from or to window not found.");
+        // Find the target touch state and touched window by fromToken.
+        auto [state, touchedWindow] = findTouchStateAndWindowLocked(fromToken);
+        if (state == nullptr || touchedWindow == nullptr) {
+            ALOGD("Focus transfer failed because from window is not being touched.");
             return false;
         }
+
+        const int32_t displayId = state->displayId;
+        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
+        if (toWindowHandle == nullptr) {
+            ALOGW("Cannot transfer focus because to window not found.");
+            return false;
+        }
+
         if (DEBUG_FOCUS) {
             ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
-                  fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
-        }
-        if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Cannot transfer focus because windows are on different displays.");
-            }
-            return false;
+                  touchedWindow->windowHandle->getName().c_str(),
+                  toWindowHandle->getName().c_str());
         }
 
-        bool found = false;
-        for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) {
-            TouchState& state = pair.second;
-            for (size_t i = 0; i < state.windows.size(); i++) {
-                const TouchedWindow& touchedWindow = state.windows[i];
-                if (touchedWindow.windowHandle == fromWindowHandle) {
-                    int32_t oldTargetFlags = touchedWindow.targetFlags;
-                    BitSet32 pointerIds = touchedWindow.pointerIds;
+        // Erase old window.
+        int32_t oldTargetFlags = touchedWindow->targetFlags;
+        BitSet32 pointerIds = touchedWindow->pointerIds;
+        state->removeWindowByToken(fromToken);
 
-                    state.windows.erase(state.windows.begin() + i);
+        // Add new window.
+        int32_t newTargetFlags = oldTargetFlags &
+                (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
+                 InputTarget::FLAG_DISPATCH_AS_IS);
+        state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
 
-                    int32_t newTargetFlags = oldTargetFlags &
-                            (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT |
-                             InputTarget::FLAG_DISPATCH_AS_IS);
-                    state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
-
-                    // Store the dragging window.
-                    if (isDragDrop) {
-                        mDragState = std::make_unique<DragState>(toWindowHandle);
-                    }
-
-                    found = true;
-                    goto Found;
-                }
-            }
-        }
-    Found:
-
-        if (!found) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Focus transfer failed because from window did not have focus.");
-            }
-            return false;
+        // Store the dragging window.
+        if (isDragDrop) {
+            mDragState = std::make_unique<DragState>(toWindowHandle);
         }
 
+        // Synthesize cancel for old window and down for new window.
         sp<Connection> fromConnection = getConnectionLocked(fromToken);
         sp<Connection> toConnection = getConnectionLocked(toToken);
         if (fromConnection != nullptr && toConnection != nullptr) {
@@ -4875,27 +4872,20 @@
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken);
+        auto it = std::find_if(mTouchStatesByDisplay.begin(), mTouchStatesByDisplay.end(),
+                               [](const auto& pair) { return pair.second.windows.size() == 1; });
+        if (it == mTouchStatesByDisplay.end()) {
+            ALOGW("Cannot transfer touch state because there is no exact window being touched");
+            return false;
+        }
+        const int32_t displayId = it->first;
+        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken, displayId);
         if (toWindowHandle == nullptr) {
             ALOGW("Could not find window associated with token=%p", destChannelToken.get());
             return false;
         }
 
-        const int32_t displayId = toWindowHandle->getInfo()->displayId;
-
-        auto touchStateIt = mTouchStatesByDisplay.find(displayId);
-        if (touchStateIt == mTouchStatesByDisplay.end()) {
-            ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched",
-                  displayId);
-            return false;
-        }
-
-        TouchState& state = touchStateIt->second;
-        if (state.windows.size() != 1) {
-            ALOGW("Cannot transfer touch state because there are %zu windows being touched",
-                  state.windows.size());
-            return false;
-        }
+        TouchState& state = it->second;
         const TouchedWindow& touchedWindow = state.windows[0];
         fromToken = touchedWindow.windowHandle->getToken();
     } // release lock
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 2436e73..1aa78d9 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -653,6 +653,10 @@
     void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
 
+    // Find touched state and touched window by token.
+    std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token)
+            REQUIRES(mLock);
+
     // Statistics gathering.
     LatencyAggregator mLatencyAggregator GUARDED_BY(mLock);
     LatencyTracker mLatencyTracker GUARDED_BY(mLock);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f884a4b..0c2f11b 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -46,7 +46,8 @@
 static const int32_t DEVICE_ID = 1;
 
 // An arbitrary display id.
-static const int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr int32_t SECOND_DISPLAY_ID = 1;
 
 // An arbitrary injector pid / uid pair that has permission to inject events.
 static const int32_t INJECTOR_PID = 999;
@@ -937,6 +938,15 @@
         mInfo.displayId = displayId;
     }
 
+    sp<FakeWindowHandle> clone(
+            const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+            const sp<InputDispatcher>& dispatcher, int32_t displayId) {
+        sp<FakeWindowHandle> handle =
+                new FakeWindowHandle(inputApplicationHandle, dispatcher, mInfo.name + "(Mirror)",
+                                     displayId, mInfo.token);
+        return handle;
+    }
+
     void setFocusable(bool focusable) { mInfo.focusable = focusable; }
 
     void setVisible(bool visible) { mInfo.visible = visible; }
@@ -1996,6 +2006,134 @@
     secondWindow->assertNoEvents();
 }
 
+// This case will create two windows and one mirrored window on the default display and mirror
+// two windows on the second display. It will test if 'transferTouchFocus' works fine if we put
+// the windows info of second display before default display.
+TEST_F(InputDispatcherTest, TransferTouchFocus_CloneSurface) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> firstWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
+    firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    sp<FakeWindowHandle> secondWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> mirrorWindowInPrimary =
+            firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
+    mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
+    mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> firstWindowInSecondary =
+            firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> secondWindowInSecondary =
+            secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    // Update window info, let it find window handle of second display first.
+    mDispatcher->setInputWindows(
+            {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
+             {ADISPLAY_ID_DEFAULT,
+              {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Window should receive motion event.
+    firstWindowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    // Transfer touch focus
+    ASSERT_TRUE(mDispatcher->transferTouchFocus(firstWindowInPrimary->getToken(),
+                                                secondWindowInPrimary->getToken()));
+    // The first window gets cancel.
+    firstWindowInPrimary->consumeMotionCancel();
+    secondWindowInPrimary->consumeMotionDown();
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionMove();
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                             {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionUp();
+}
+
+// Same as TransferTouchFocus_CloneSurface, but this touch on the secondary display and use
+// 'transferTouch' api.
+TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> firstWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
+    firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+    sp<FakeWindowHandle> secondWindowInPrimary =
+            new FakeWindowHandle(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> mirrorWindowInPrimary =
+            firstWindowInPrimary->clone(application, mDispatcher, ADISPLAY_ID_DEFAULT);
+    mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
+    mirrorWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> firstWindowInSecondary =
+            firstWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    firstWindowInSecondary->setFrame(Rect(0, 0, 100, 100));
+    firstWindowInSecondary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    sp<FakeWindowHandle> secondWindowInSecondary =
+            secondWindowInPrimary->clone(application, mDispatcher, SECOND_DISPLAY_ID);
+    secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
+    secondWindowInPrimary->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+
+    // Update window info, let it find window handle of second display first.
+    mDispatcher->setInputWindows(
+            {{SECOND_DISPLAY_ID, {firstWindowInSecondary, secondWindowInSecondary}},
+             {ADISPLAY_ID_DEFAULT,
+              {mirrorWindowInPrimary, firstWindowInPrimary, secondWindowInPrimary}}});
+
+    // Touch on second display.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    // Window should receive motion event.
+    firstWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
+
+    // Transfer touch focus
+    ASSERT_TRUE(mDispatcher->transferTouch(secondWindowInSecondary->getToken()));
+
+    // The first window gets cancel.
+    firstWindowInPrimary->consumeMotionCancel(SECOND_DISPLAY_ID);
+    secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                SECOND_DISPLAY_ID, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID);
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    firstWindowInPrimary->assertNoEvents();
+    secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID);
+}
+
 TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
@@ -2906,7 +3044,6 @@
 /* Test InputDispatcher for MultiDisplay */
 class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
 public:
-    static constexpr int32_t SECOND_DISPLAY_ID = 1;
     virtual void SetUp() override {
         InputDispatcherTest::SetUp();
 
@@ -3069,8 +3206,6 @@
 
 class InputFilterTest : public InputDispatcherTest {
 protected:
-    static constexpr int32_t SECOND_DISPLAY_ID = 1;
-
     void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
         NotifyMotionArgs motionArgs;