Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 105a3fca authored by Arpit Singh's avatar Arpit Singh
Browse files

[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
parent 43a1d490
Loading
Loading
Loading
Loading
+40 −3
Original line number Diff line number Diff line
@@ -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;
+11 −0
Original line number Diff line number Diff line
@@ -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;
+77 −20
Original line number Diff line number Diff line
@@ -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
        // Spy window should also receive motion event if event is on the same display.
        if (displayId == ui::LogicalDisplayId::DEFAULT) {
            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