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

Commit 6abcf8ff authored by Vaibhav Devmurari's avatar Vaibhav Devmurari
Browse files

Call pilfer pointers when second pointer goes down during D&D

We added new functionality to pilferPointers API in ag/18234604.
Using it we can extend the functionality of drag and drop to
allow user to interact with the UI while using 1 pointer/finger
to drag. Pilfering pointers allows gesture monitors to restart
gesture detection with new pointers that down.
Avoid pilfering right after transferTouch on D&D to prevent
security bug: Apps can misuse startDragAndDrop() API to keep
pilfering pointers for gesture monitors and user can get stuck
in an app. Instead we will pilfer when a second pointer goes down
indicating user want to interact while using D&D.

DD: go/global_drag_and_drop

Bug: 220109830

Test: atest inputflinger_tests
Change-Id: I956f040ff307021bb2f74a06228c6addb90e2168
parent eb84c421
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -1657,6 +1657,13 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<
    InputEventInjectionResult injectionResult;
    if (isPointerEvent) {
        // Pointer event.  (eg. touchscreen)

        if (mDragState &&
            (entry->action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_POINTER_DOWN) {
            // If drag and drop ongoing and pointer down occur: pilfer drag window pointers
            pilferPointersLocked(mDragState->dragWindow->getToken());
        }

        injectionResult =
                findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
                                               &conflictingPointerActions);
@@ -5575,7 +5582,10 @@ void InputDispatcher::removeMonitorChannelLocked(const sp<IBinder>& connectionTo

status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) {
    std::scoped_lock _l(mLock);
    return pilferPointersLocked(token);
}

status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) {
    const std::shared_ptr<InputChannel> requestingChannel = getInputChannelLocked(token);
    if (!requestingChannel) {
        ALOGW("Attempted to pilfer pointers from an un-registered channel or invalid token");
+2 −0
Original line number Diff line number Diff line
@@ -255,6 +255,8 @@ private:

    void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);

    status_t pilferPointersLocked(const sp<IBinder>& token) REQUIRES(mLock);

    template <typename T>
    struct StrongPointerHash {
        std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); }
+35 −2
Original line number Diff line number Diff line
@@ -6094,6 +6094,7 @@ protected:
    sp<FakeWindowHandle> mWindow;
    sp<FakeWindowHandle> mSecondWindow;
    sp<FakeWindowHandle> mDragWindow;
    sp<FakeWindowHandle> mSpyWindow;

    void SetUp() override {
        InputDispatcherTest::SetUp();
@@ -6104,8 +6105,13 @@ protected:
        mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
        mSecondWindow->setFrame(Rect(100, 0, 200, 100));

        mSpyWindow = new FakeWindowHandle(mApp, mDispatcher, "SpyWindow", ADISPLAY_ID_DEFAULT);
        mSpyWindow->setSpy(true);
        mSpyWindow->setTrustedOverlay(true);
        mSpyWindow->setFrame(Rect(0, 0, 200, 100));

        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mSpyWindow, mWindow, mSecondWindow}}});
    }

    void injectDown() {
@@ -6116,6 +6122,8 @@ protected:

        // Window should receive motion event.
        mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
        // Spy window should also receive motion event
        mSpyWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
    }

    // Start performing drag, we will create a drag window and transfer touch to it.
@@ -6128,8 +6136,9 @@ protected:

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

        // Transfer touch focus to the drag window
        bool transferred =
@@ -6207,6 +6216,30 @@ TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
    mSecondWindow->assertNoEvents();
}

TEST_F(InputDispatcherDragTests, DragEnterAndPointerDownPilfersPointers) {
    performDrag();

    // No cancel event after drag start
    mSpyWindow->assertNoEvents();

    const MotionEvent secondFingerDownEvent =
            MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(60).y(60))
                    .build();
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
                                InputEventInjectionSync::WAIT_FOR_RESULT))
            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";

    // Receives cancel for first pointer after next pointer down
    mSpyWindow->consumeMotionCancel();
    mSpyWindow->consumeMotionDown();

    mSpyWindow->assertNoEvents();
}

TEST_F(InputDispatcherDragTests, DragAndDrop) {
    performDrag();