diff options
author | 2025-02-18 10:31:11 +0000 | |
---|---|---|
committer | 2025-02-19 09:36:57 +0000 | |
commit | 105a3fca94924ef82f67ed654bbe91fdc41c6487 (patch) | |
tree | 1fb882a2865011a6161baf508c9ff2948b8cf1d2 | |
parent | 43a1d490549ede9c1278b04484f43a9acbeb535e (diff) |
[CD Cursor] Enable gesture transfer across connected displays
In case of a mouse touchState's displayId may not be same as touched
window's displayId. This causes the gesture transfer for drag and drop
to fail. To prevent this we should lookup for transfer target on all
connected displays.
Test: atest inputflinger_tests
Bug: 393344208
Flag: com.android.input.flags.connected_displays_cursor
Change-Id: Ib193f108c54655f2170585ae02228cda969977ea
-rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 43 | ||||
-rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.h | 11 | ||||
-rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 97 |
3 files changed, 128 insertions, 23 deletions
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 04197ee99d..ba7507133e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -5067,8 +5067,19 @@ sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandle( } // Only look through the requested display. - for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(*displayId)) { - if (windowHandle->getToken() == windowHandleToken) { + return findWindowHandleOnDisplay(windowHandleToken, *displayId); +} + +sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnConnectedDisplays( + const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const { + if (windowHandleToken == nullptr) { + return nullptr; + } + + sp<WindowInfoHandle> windowHandle; + for (ui::LogicalDisplayId connectedDisplayId : getConnectedDisplays(displayId)) { + windowHandle = findWindowHandleOnDisplay(windowHandleToken, connectedDisplayId); + if (windowHandle != nullptr) { return windowHandle; } } @@ -5214,6 +5225,29 @@ std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() co return dump; } +std::vector<ui::LogicalDisplayId> InputDispatcher::DispatcherWindowInfo::getConnectedDisplays( + ui::LogicalDisplayId displayId) const { + if (!mTopology.graph.contains(displayId)) { + return {displayId}; + } + + std::vector<ui::LogicalDisplayId> connectedDisplays; + for (auto it : mTopology.graph) { + connectedDisplays.push_back(it.first); + } + return connectedDisplays; +} + +sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnDisplay( + const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const { + for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(displayId)) { + if (windowHandle->getToken() == windowHandleToken) { + return windowHandle; + } + } + return nullptr; +} + bool InputDispatcher::DispatcherTouchState::canWindowReceiveMotion( const sp<android::gui::WindowInfoHandle>& window, const android::inputdispatcher::MotionEntry& motionEntry) const { @@ -5806,7 +5840,10 @@ InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IB const DeviceId deviceId = *deviceIds.begin(); const sp<WindowInfoHandle> fromWindowHandle = touchedWindow.windowHandle; - const sp<WindowInfoHandle> toWindowHandle = mWindowInfos.findWindowHandle(toToken, displayId); + // TouchState displayId may not be same as window displayId, we need to lookup for toToken on + // all connected displays. + const sp<WindowInfoHandle> toWindowHandle = + mWindowInfos.findWindowHandleOnConnectedDisplays(toToken, displayId); if (!toWindowHandle) { ALOGW("Cannot transfer touch because the transfer target window was not found."); return std::nullopt; diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 4b4996dbca..38f782573a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -319,6 +319,11 @@ private: const sp<IBinder>& windowHandleToken, std::optional<ui::LogicalDisplayId> displayId = {}) const; + // Lookup for WindowInfoHandle from token and a display-id. Lookup is done for all connected + // displays in the topology of the queried display. + sp<android::gui::WindowInfoHandle> findWindowHandleOnConnectedDisplays( + const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const; + bool isWindowPresent(const sp<android::gui::WindowInfoHandle>& windowHandle) const; // Returns the touched window at the given location, excluding the ignoreWindow if provided. @@ -349,6 +354,12 @@ private: std::string dumpDisplayAndWindowInfo() const; private: + std::vector<ui::LogicalDisplayId> getConnectedDisplays( + ui::LogicalDisplayId displayId) const; + + sp<android::gui::WindowInfoHandle> findWindowHandleOnDisplay( + const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const; + std::unordered_map<ui::LogicalDisplayId /*displayId*/, std::vector<sp<android::gui::WindowInfoHandle>>> mWindowHandlesByDisplay; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 6c5d94d984..1778f6d508 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -12414,18 +12414,22 @@ protected: 0}); } - void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) { + void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN, + ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT) { bool consumeButtonPress = false; + const PointF location = + displayId == ui::LogicalDisplayId::DEFAULT ? PointF(50, 50) : PointF(50, 450); switch (fromSource) { case AINPUT_SOURCE_TOUCHSCREEN: { ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, - ui::LogicalDisplayId::DEFAULT, {50, 50})) + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, displayId, + location)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; break; } case AINPUT_SOURCE_STYLUS: { - PointerBuilder pointer = PointerBuilder(0, ToolType::STYLUS).x(50).y(50); + PointerBuilder pointer = + PointerBuilder(0, ToolType::STYLUS).x(location.x).y(location.y); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, @@ -12448,12 +12452,14 @@ protected: break; } case AINPUT_SOURCE_MOUSE: { - PointerBuilder pointer = - PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50); + PointerBuilder pointer = PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE) + .x(location.x) + .y(location.y); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .displayId(displayId) .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) .pointer(pointer) .build())); @@ -12461,6 +12467,7 @@ protected: injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE) + .displayId(displayId) .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) .pointer(pointer) @@ -12474,25 +12481,30 @@ protected: } // Window should receive motion event. - mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT); + sp<FakeWindowHandle>& targetWindow = + displayId == ui::LogicalDisplayId::DEFAULT ? mWindow : mWindowOnSecondDisplay; + targetWindow->consumeMotionDown(displayId); if (consumeButtonPress) { - mWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + targetWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + } + + // Spy window should also receive motion event if event is on the same display. + if (displayId == ui::LogicalDisplayId::DEFAULT) { + mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT); } - // Spy window should also receive motion event - mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT); } // Start performing drag, we will create a drag window and transfer touch to it. // @param sendDown : if true, send a motion down on first window before perform drag and drop. // Returns true on success. - bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN) { + bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN, + ui::LogicalDisplayId dragStartDisplay = ui::LogicalDisplayId::DEFAULT) { if (sendDown) { - injectDown(fromSource); + injectDown(fromSource, dragStartDisplay); } // The drag window covers the entire display - mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", - ui::LogicalDisplayId::DEFAULT); + mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", dragStartDisplay); mDragWindow->setTouchableRegion(Region{{0, 0, 0, 0}}); mDispatcher->onWindowInfosChanged( {{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(), @@ -12501,14 +12513,17 @@ protected: 0, 0}); + sp<FakeWindowHandle>& targetWindow = dragStartDisplay == ui::LogicalDisplayId::DEFAULT + ? mWindow + : mWindowOnSecondDisplay; + // Transfer touch focus to the drag window bool transferred = - mDispatcher->transferTouchGesture(mWindow->getToken(), mDragWindow->getToken(), + mDispatcher->transferTouchGesture(targetWindow->getToken(), mDragWindow->getToken(), /*isDragDrop=*/true); if (transferred) { - mWindow->consumeMotionCancel(); - mDragWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT, - AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + targetWindow->consumeMotionCancel(dragStartDisplay); + mDragWindow->consumeMotionDown(dragStartDisplay, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); } return transferred; } @@ -15292,10 +15307,10 @@ TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseGesture) { mWindow->consumeMotionUp(SECOND_DISPLAY_ID); } -TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDrop) { +TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromPrimaryDisplay) { SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); - startDrag(true, AINPUT_SOURCE_MOUSE); + EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE)); // Move on window. mDispatcher->notifyMotion( MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) @@ -15346,4 +15361,46 @@ TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDrop) { mWindowOnSecondDisplay->assertNoEvents(); } +TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromNonPrimaryDisplay) { + SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); + + EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE, SECOND_DISPLAY_ID)); + // Move on window. + mDispatcher->notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) + .displayId(SECOND_DISPLAY_ID) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) + .build()); + mDragWindow->consumeMotionMove(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + mWindow->assertNoEvents(); + mSecondWindow->assertNoEvents(); + mWindowOnSecondDisplay->consumeDragEvent(false, 50, 50); + + // Move to window on the primary display + mDispatcher->notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) + .displayId(DISPLAY_ID) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) + .build()); + mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + mWindow->consumeDragEvent(false, 50, 50); + mSecondWindow->assertNoEvents(); + mWindowOnSecondDisplay->consumeDragEvent(true, 50, 50); + + // drop on the primary display + mDispatcher->notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE) + .displayId(DISPLAY_ID) + .buttonState(0) + .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) + .build()); + mDragWindow->consumeMotionUp(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken()); + mWindow->assertNoEvents(); + mSecondWindow->assertNoEvents(); + mWindowOnSecondDisplay->assertNoEvents(); +} + } // namespace android::inputdispatcher |