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

Commit 82dc0426 authored by Siarhei Vishniakou's avatar Siarhei Vishniakou
Browse files

Don't store the departing pointer in InputState

Before this CL, InputState stored the pointer that is leaving.
This is a problem when the request to cancel the current gesture comes
in. To cancel the gesture, the currently stored pointers were used. That
means that in a sequence of ACTION_DOWN -> ACTION_POINTER_DOWN ->
ACTION_POINTER_UP -> CANCEL, the cancel event would still be produced
with two pointers.

The correct behaviour to is to cancel the remaining pointer.

The solution here skips the addition of the departing pointer to the
InputState, and modifies the pointerCount appropriately.

Bug: 211379801
Test: m inputflinger_tests && $ANDROID_HOST_OUT/nativetest64/inputflinger_tests/inputflinger_tests --gtest_filter="*CancelAfterPointer0Up*"
Merged-In: I8323cc08a974d0ec880b5570f0cd572ee071522a
Change-Id: I8323cc08a974d0ec880b5570f0cd572ee071522a
(cherry picked from commit be530ebf)
parent 42b349a2
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -247,10 +247,19 @@ void InputState::addMotionMemento(const MotionEntry& entry, int32_t flags, bool
}

void InputState::MotionMemento::setPointers(const MotionEntry& entry) {
    pointerCount = entry.pointerCount;
    pointerCount = 0;
    for (uint32_t i = 0; i < entry.pointerCount; i++) {
        pointerProperties[i].copyFrom(entry.pointerProperties[i]);
        pointerCoords[i].copyFrom(entry.pointerCoords[i]);
        if (MotionEvent::getActionMasked(entry.action) == AMOTION_EVENT_ACTION_POINTER_UP) {
            // In POINTER_UP events, the pointer is leaving. Since the action is not stored,
            // this departing pointer should not be recorded.
            const uint8_t actionIndex = MotionEvent::getActionIndex(entry.action);
            if (i == actionIndex) {
                continue;
            }
        }
        pointerProperties[pointerCount].copyFrom(entry.pointerProperties[i]);
        pointerCoords[pointerCount].copyFrom(entry.pointerCoords[i]);
        pointerCount++;
    }
}

+43 −1
Original line number Diff line number Diff line
@@ -1944,6 +1944,48 @@ TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCance
    wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
}

/**
 * Two fingers down on the window, and lift off the first finger.
 * Next, cancel the gesture to the window by removing the window. Make sure that the CANCEL event
 * contains a single pointer.
 */
TEST_F(InputDispatcherTest, CancelAfterPointer0Up) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);

    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
    NotifyMotionArgs args;
    // First touch pointer down on right window
    mDispatcher->notifyMotion(&(
            args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                           .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
                           .build()));
    // Second touch pointer down
    mDispatcher->notifyMotion(&(
            args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)

                           .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
                           .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(110).y(100))
                           .build()));
    // First touch pointer lifts. The second one remains down
    mDispatcher->notifyMotion(&(
            args = MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)

                           .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100))
                           .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(110).y(100))
                           .build()));
    window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
    window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
    window->consumeMotionEvent(WithMotionAction(POINTER_0_UP));

    // Remove the window. The gesture should be canceled
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
    const std::map<int32_t, PointF> expectedPointers{{1, PointF{110, 100}}};
    window->consumeMotionEvent(
            AllOf(WithMotionAction(ACTION_CANCEL), WithPointers(expectedPointers)));
}

/**
 * Same test as WhenForegroundWindowDisappears_WallpaperTouchIsCanceled above,
 * with the following differences:
@@ -2523,7 +2565,7 @@ TEST_F(InputDispatcherTest, MixedTouchAndMouseWithPointerDown) {
                           .build()));

    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId),
                                     WithPointerCount(2u)));
                                     WithPointerCount(1u)));
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));

    mDispatcher->notifyMotion(&(