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

Commit b75c2aa9 authored by Arthur Hung's avatar Arthur Hung
Browse files

Fix drag and drop access wrong pointer id

If the touched window didn't support split, it won't track the
pointerIds. When the drag and drop started, it would rely on the
pointerIds to check current touch state and store the initial drag
pointer id but it would always be 0.

This CL will let pointerIds could track all down pointers when touched
windows received down or pointer down, so it could access the right
pointer id from current touch state.

Test: atest inputflinger_tests CrossAppDragAndDropTests
Bug: 237233207
Change-Id: Ia5d91814a2aca56095c29b029e35e66cd669bce9
parent 883349a8
Loading
Loading
Loading
Loading
+28 −27
Original line number Original line Diff line number Diff line
@@ -2207,10 +2207,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(


            // Update the temporary touch state.
            // Update the temporary touch state.
            BitSet32 pointerIds;
            BitSet32 pointerIds;
            if (isSplit) {
            pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
                uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
                pointerIds.markBit(pointerId);
            }


            tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds,
            tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds,
                                             entry.eventTime);
                                             entry.eventTime);
@@ -2297,9 +2294,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
                }
                }


                BitSet32 pointerIds;
                BitSet32 pointerIds;
                if (isSplit) {
                pointerIds.markBit(entry.pointerProperties[0].id);
                pointerIds.markBit(entry.pointerProperties[0].id);
                }
                tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds,
                tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds,
                                                 entry.eventTime);
                                                 entry.eventTime);
            }
            }
@@ -2477,21 +2472,28 @@ Failed:
            }
            }
        } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
        } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
            // One pointer went up.
            // One pointer went up.
            if (isSplit) {
            int32_t pointerIndex = getMotionEventActionPointerIndex(action);
            int32_t pointerIndex = getMotionEventActionPointerIndex(action);
            uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
            uint32_t pointerId = entry.pointerProperties[pointerIndex].id;


            for (size_t i = 0; i < tempTouchState.windows.size();) {
            for (size_t i = 0; i < tempTouchState.windows.size();) {
                TouchedWindow& touchedWindow = tempTouchState.windows[i];
                TouchedWindow& touchedWindow = tempTouchState.windows[i];
                    if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
                touchedWindow.pointerIds.clearBit(pointerId);
                touchedWindow.pointerIds.clearBit(pointerId);
                if (touchedWindow.pointerIds.isEmpty()) {
                if (touchedWindow.pointerIds.isEmpty()) {
                    tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
                    tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
                    continue;
                    continue;
                }
                }
                    }
                i += 1;
                i += 1;
            }
            }
        } else if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
            // If no split, we suppose all touched windows should receive pointer down.
            const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
            for (size_t i = 0; i < tempTouchState.windows.size(); i++) {
                TouchedWindow& touchedWindow = tempTouchState.windows[i];
                // Ignore drag window for it should just track one pointer.
                if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) {
                    continue;
                }
                touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id);
            }
            }
        }
        }


@@ -5121,14 +5123,13 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<


        // Store the dragging window.
        // Store the dragging window.
        if (isDragDrop) {
        if (isDragDrop) {
            if (pointerIds.count() > 1) {
            if (pointerIds.count() != 1) {
                ALOGW("The drag and drop cannot be started when there is more than 1 pointer on the"
                ALOGW("The drag and drop cannot be started when there is no pointer or more than 1"
                      " window.");
                      " pointer on the window.");
                return false;
                return false;
            }
            }
            // If the window didn't not support split or the source is mouse, the pointerIds count
            // Track the pointer id for drag window and generate the drag state.
            // would be 0, so we have to track the pointer 0.
            const int32_t id = pointerIds.firstMarkedBit();
            const int32_t id = pointerIds.count() == 0 ? 0 : pointerIds.firstMarkedBit();
            mDragState = std::make_unique<DragState>(toWindowHandle, id);
            mDragState = std::make_unique<DragState>(toWindowHandle, id);
        }
        }


+2 −2
Original line number Original line Diff line number Diff line
@@ -43,8 +43,8 @@ std::string dispatchModeToString(int32_t dispatchMode) {
}
}


void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) {
void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) {
    // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
    // The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no
    // and non splittable windows since we will just use all the pointers from the input event.
    // valid pointer property from the input event.
    if (newPointerIds.isEmpty()) {
    if (newPointerIds.isEmpty()) {
        setDefaultPointerTransform(transform);
        setDefaultPointerTransform(transform);
        return;
        return;
+1 −1
Original line number Original line Diff line number Diff line
@@ -29,7 +29,7 @@ namespace inputdispatcher {
struct TouchedWindow {
struct TouchedWindow {
    sp<gui::WindowInfoHandle> windowHandle;
    sp<gui::WindowInfoHandle> windowHandle;
    int32_t targetFlags;
    int32_t targetFlags;
    BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set
    BitSet32 pointerIds;
    bool isPilferingPointers = false;
    bool isPilferingPointers = false;
    // Time at which the first action down occurred on this window.
    // Time at which the first action down occurred on this window.
    // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE scenario.
    // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE scenario.
+99 −41
Original line number Original line Diff line number Diff line
@@ -6173,6 +6173,8 @@ protected:
    sp<FakeWindowHandle> mSecondWindow;
    sp<FakeWindowHandle> mSecondWindow;
    sp<FakeWindowHandle> mDragWindow;
    sp<FakeWindowHandle> mDragWindow;
    sp<FakeWindowHandle> mSpyWindow;
    sp<FakeWindowHandle> mSpyWindow;
    // Mouse would force no-split, set the id as non-zero to verify if drag state could track it.
    static constexpr int32_t MOUSE_POINTER_ID = 1;


    void SetUp() override {
    void SetUp() override {
        InputDispatcherTest::SetUp();
        InputDispatcherTest::SetUp();
@@ -6192,11 +6194,41 @@ protected:
        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mSpyWindow, mWindow, mSecondWindow}}});
        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mSpyWindow, mWindow, mSecondWindow}}});
    }
    }


    void injectDown() {
    void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
        switch (fromSource) {
            case AINPUT_SOURCE_TOUCHSCREEN:
                ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
                ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
                  injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                          injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
                                   {50, 50}))
                                           ADISPLAY_ID_DEFAULT, {50, 50}))
                        << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
                        << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
                break;
            case AINPUT_SOURCE_STYLUS:
                ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
                          injectMotionEvent(
                                  mDispatcher,
                                  MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
                                                     AINPUT_SOURCE_STYLUS)
                                          .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
                                          .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
                                                           .x(50)
                                                           .y(50))
                                          .build()));
                break;
            case AINPUT_SOURCE_MOUSE:
                ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
                          injectMotionEvent(
                                  mDispatcher,
                                  MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
                                          .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
                                          .pointer(PointerBuilder(MOUSE_POINTER_ID,
                                                                  AMOTION_EVENT_TOOL_TYPE_MOUSE)
                                                           .x(50)
                                                           .y(50))
                                          .build()));
                break;
            default:
                FAIL() << "Source " << fromSource << " doesn't support drag and drop";
        }


        // Window should receive motion event.
        // Window should receive motion event.
        mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
        mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -6207,9 +6239,9 @@ protected:
    // Start performing drag, we will create a drag window and transfer touch to it.
    // 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.
    // @param sendDown : if true, send a motion down on first window before perform drag and drop.
    // Returns true on success.
    // Returns true on success.
    bool performDrag(bool sendDown = true) {
    bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
        if (sendDown) {
        if (sendDown) {
            injectDown();
            injectDown(fromSource);
        }
        }


        // The drag window covers the entire display
        // The drag window covers the entire display
@@ -6228,36 +6260,10 @@ protected:
        }
        }
        return transferred;
        return transferred;
    }
    }

    // Start performing drag, we will create a drag window and transfer touch to it.
    void performStylusDrag() {
        ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
                  injectMotionEvent(mDispatcher,
                                    MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
                                                       AINPUT_SOURCE_STYLUS)
                                            .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY)
                                            .pointer(PointerBuilder(0,
                                                                    AMOTION_EVENT_TOOL_TYPE_STYLUS)
                                                             .x(50)
                                                             .y(50))
                                            .build()));
        mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);

        // The drag window covers the entire display
        mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
        mDispatcher->setInputWindows(
                {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});

        // Transfer touch focus to the drag window
        mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
                                        true /* isDragDrop */);
        mWindow->consumeMotionCancel();
        mDragWindow->consumeMotionDown();
    }
};
};


TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
    performDrag();
    startDrag();


    // Move on window.
    // Move on window.
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6295,7 +6301,7 @@ TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
}
}


TEST_F(InputDispatcherDragTests, DragEnterAndPointerDownPilfersPointers) {
TEST_F(InputDispatcherDragTests, DragEnterAndPointerDownPilfersPointers) {
    performDrag();
    startDrag();


    // No cancel event after drag start
    // No cancel event after drag start
    mSpyWindow->assertNoEvents();
    mSpyWindow->assertNoEvents();
@@ -6319,7 +6325,7 @@ TEST_F(InputDispatcherDragTests, DragEnterAndPointerDownPilfersPointers) {
}
}


TEST_F(InputDispatcherDragTests, DragAndDrop) {
TEST_F(InputDispatcherDragTests, DragAndDrop) {
    performDrag();
    startDrag();


    // Move on window.
    // Move on window.
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6351,7 +6357,7 @@ TEST_F(InputDispatcherDragTests, DragAndDrop) {
}
}


TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
    performStylusDrag();
    startDrag(true, AINPUT_SOURCE_STYLUS);


    // Move on window and keep button pressed.
    // Move on window and keep button pressed.
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6398,7 +6404,7 @@ TEST_F(InputDispatcherDragTests, StylusDragAndDrop) {
}
}


TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) {
TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) {
    performDrag();
    startDrag();


    // Set second window invisible.
    // Set second window invisible.
    mSecondWindow->setVisible(false);
    mSecondWindow->setVisible(false);
@@ -6434,6 +6440,9 @@ TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) {
}
}


TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) {
TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) {
    // Ensure window could track pointerIds if it didn't support split touch.
    mWindow->setPreventSplitting(true);

    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                               {50, 50}))
                               {50, 50}))
@@ -6454,7 +6463,7 @@ TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) {
    mWindow->consumeMotionPointerDown(1 /* pointerIndex */);
    mWindow->consumeMotionPointerDown(1 /* pointerIndex */);


    // Should not perform drag and drop when window has multi fingers.
    // Should not perform drag and drop when window has multi fingers.
    ASSERT_FALSE(performDrag(false));
    ASSERT_FALSE(startDrag(false));
}
}


TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
@@ -6482,7 +6491,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
    mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
    mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);


    // Perform drag and drop from first window.
    // Perform drag and drop from first window.
    ASSERT_TRUE(performDrag(false));
    ASSERT_TRUE(startDrag(false));


    // Move on window.
    // Move on window.
    const MotionEvent secondFingerMoveEvent =
    const MotionEvent secondFingerMoveEvent =
@@ -6517,7 +6526,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
}
}


TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) {
TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) {
    performDrag();
    startDrag();


    // Update window of second display.
    // Update window of second display.
    sp<FakeWindowHandle> windowInSecondary =
    sp<FakeWindowHandle> windowInSecondary =
@@ -6568,6 +6577,55 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) {
    mSecondWindow->assertNoEvents();
    mSecondWindow->assertNoEvents();
}
}


TEST_F(InputDispatcherDragTests, MouseDragAndDrop) {
    startDrag(true, AINPUT_SOURCE_MOUSE);
    // Move on window.
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionEvent(mDispatcher,
                                MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
                                        .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
                                        .pointer(PointerBuilder(MOUSE_POINTER_ID,
                                                                AMOTION_EVENT_TOOL_TYPE_MOUSE)
                                                         .x(50)
                                                         .y(50))
                                        .build()))
            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
    mWindow->consumeDragEvent(false, 50, 50);
    mSecondWindow->assertNoEvents();

    // Move to another window.
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionEvent(mDispatcher,
                                MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
                                        .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
                                        .pointer(PointerBuilder(MOUSE_POINTER_ID,
                                                                AMOTION_EVENT_TOOL_TYPE_MOUSE)
                                                         .x(150)
                                                         .y(50))
                                        .build()))
            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
    mWindow->consumeDragEvent(true, 150, 50);
    mSecondWindow->consumeDragEvent(false, 50, 50);

    // drop to another window.
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionEvent(mDispatcher,
                                MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
                                        .buttonState(0)
                                        .pointer(PointerBuilder(MOUSE_POINTER_ID,
                                                                AMOTION_EVENT_TOOL_TYPE_MOUSE)
                                                         .x(150)
                                                         .y(50))
                                        .build()))
            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
    mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
    mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken());
    mWindow->assertNoEvents();
    mSecondWindow->assertNoEvents();
}

class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};


TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {