summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Arpit Singh <arpitks@google.com> 2025-02-18 10:31:11 +0000
committer Arpit Singh <arpitks@google.com> 2025-02-19 09:36:57 +0000
commit105a3fca94924ef82f67ed654bbe91fdc41c6487 (patch)
tree1fb882a2865011a6161baf508c9ff2948b8cf1d2
parent43a1d490549ede9c1278b04484f43a9acbeb535e (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.cpp43
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.h11
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp97
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