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

Commit 190ac25e authored by Arthur Hung's avatar Arthur Hung Committed by Android (Google) Code Review
Browse files

Merge "Fix drag and drop access wrong pointer id" into tm-qpr-dev

parents b12f59fa 02701603
Loading
Loading
Loading
Loading
+28 −27
Original line number Diff line number Diff line
@@ -2195,10 +2195,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(

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

            tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds);
        }
@@ -2275,9 +2272,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
                }

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

            for (size_t i = 0; i < tempTouchState.windows.size();) {
                TouchedWindow& touchedWindow = tempTouchState.windows[i];
                    if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
                touchedWindow.pointerIds.clearBit(pointerId);
                if (touchedWindow.pointerIds.isEmpty()) {
                    tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
                    continue;
                }
                    }
                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);
            }
        }

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

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

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

void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) {
    // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
    // and non splittable windows since we will just use all the pointers from the input event.
    // The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no
    // valid pointer property from the input event.
    if (newPointerIds.isEmpty()) {
        setDefaultPointerTransform(transform);
        return;
+1 −1
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ namespace inputdispatcher {
struct TouchedWindow {
    sp<gui::WindowInfoHandle> windowHandle;
    int32_t targetFlags;
    BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set
    BitSet32 pointerIds;
};

} // namespace inputdispatcher
+98 −40
Original line number Diff line number Diff line
@@ -6125,6 +6125,8 @@ protected:
    sp<FakeWindowHandle> mWindow;
    sp<FakeWindowHandle> mSecondWindow;
    sp<FakeWindowHandle> mDragWindow;
    // 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 {
        InputDispatcherTest::SetUp();
@@ -6139,11 +6141,41 @@ protected:
        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
    }

    void injectDown() {
    void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
        switch (fromSource) {
            case AINPUT_SOURCE_TOUCHSCREEN:
                ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
                  injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                   {50, 50}))
                          injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
                                           ADISPLAY_ID_DEFAULT, {50, 50}))
                        << "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.
        mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
@@ -6152,9 +6184,9 @@ protected:
    // 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 performDrag(bool sendDown = true) {
    bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN) {
        if (sendDown) {
            injectDown();
            injectDown(fromSource);
        }

        // The drag window covers the entire display
@@ -6172,36 +6204,10 @@ protected:
        }
        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) {
    performDrag();
    startDrag();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    // Update window of second display.
    sp<FakeWindowHandle> windowInSecondary =
@@ -6488,6 +6497,55 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) {
    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 {};

TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {