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

Commit 2e3e443f authored by Siarhei Vishniakou's avatar Siarhei Vishniakou
Browse files

Ignore hover events while pointers are already down

If pointers are already down from another device, finish the current
gesture and ignore events from other devices.

When multi-device support is enabled, we can remove this restriction.

Bug: 268538505
Test: m inputflinger_tests && $ANDROID_HOST_OUT/nativetest64/inputflinger_tests/inputflinger_tests
Change-Id: I6419b6d118760d285457bc9e5127e055f21ecf7f
parent 858a6a88
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ struct CancelationOptions {
        CANCEL_POINTER_EVENTS = 1,
        CANCEL_NON_POINTER_EVENTS = 2,
        CANCEL_FALLBACK_EVENTS = 3,
        ftl_last = CANCEL_FALLBACK_EVENTS,
    };

    // The criterion to use to determine which events should be canceled.
+5 −4
Original line number Diff line number Diff line
@@ -2185,8 +2185,9 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
    const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);

    if (newGesture) {
        bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
        if (switchedDevice && tempTouchState.isDown() && !down && !isHoverAction) {
        // If pointers are already down, let's finish the current gesture and ignore the new events
        // from another device.
        if (switchedDevice && wasDown) {
            ALOGI("Dropping event because a pointer for a different device is already down "
                  "in display %" PRId32,
                  displayId);
@@ -3761,9 +3762,9 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
    }
    if (DEBUG_OUTBOUND_EVENT_DETAILS) {
        ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
              "with reality: %s, mode=%d.",
              "with reality: %s, mode=%s.",
              connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
              options.mode);
              ftl::enum_string(options.mode).c_str());
    }

    std::string reason = std::string("reason=").append(options.reason);
+80 −0
Original line number Diff line number Diff line
@@ -2267,6 +2267,86 @@ TEST_F(InputDispatcherTest, HoverTapAndSplitTouch) {
    rightWindow->assertNoEvents();
}

/**
 * Start hovering with a stylus device, and then tap with a touch device. Ensure no crash occurs.
 * While the touch is down, new hover events from the stylus device should be ignored. After the
 * touch is gone, stylus hovering should start working again.
 */
TEST_F(InputDispatcherTest, StylusHoverAndTouchTap) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
    window->setFrame(Rect(0, 0, 200, 200));

    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});

    const int32_t stylusDeviceId = 5;
    const int32_t touchDeviceId = 4;
    // Start hovering with stylus
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionEvent(mDispatcher,
                                MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
                                                   AINPUT_SOURCE_STYLUS)
                                        .deviceId(stylusDeviceId)
                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
                                                         .x(50)
                                                         .y(50))
                                        .build()));
    window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));

    // Finger down on the window
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionEvent(mDispatcher,
                                MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
                                                   AINPUT_SOURCE_TOUCHSCREEN)
                                        .deviceId(touchDeviceId)
                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER)
                                                         .x(100)
                                                         .y(100))
                                        .build()));
    window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
    window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));

    // Try to continue hovering with stylus. Since we are already down, injection should fail
    ASSERT_EQ(InputEventInjectionResult::FAILED,
              injectMotionEvent(mDispatcher,
                                MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
                                                   AINPUT_SOURCE_STYLUS)
                                        .deviceId(stylusDeviceId)
                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
                                                         .x(50)
                                                         .y(50))
                                        .build()));
    // No event should be sent. This event should be ignored because a pointer from another device
    // is already down.

    // Lift up the finger
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionEvent(mDispatcher,
                                MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
                                                   AINPUT_SOURCE_TOUCHSCREEN)
                                        .deviceId(touchDeviceId)
                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER)
                                                         .x(100)
                                                         .y(100))
                                        .build()));
    window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));

    // Now that the touch is gone, stylus hovering should start working again
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionEvent(mDispatcher,
                                MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
                                                   AINPUT_SOURCE_STYLUS)
                                        .deviceId(stylusDeviceId)
                                        .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS)
                                                         .x(50)
                                                         .y(50))
                                        .build()));
    window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
    // No more events
    window->assertNoEvents();
}

/**
 * On the display, have a single window, and also an area where there's no window.
 * First pointer touches the "no window" area of the screen. Second pointer touches the window.