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

Commit 8c3375bb authored by Siarhei Vishniakou's avatar Siarhei Vishniakou Committed by Android (Google) Code Review
Browse files

Merge "inputdispatcher: Block touch while stylus is hovering" into main

parents 40663e8b 3ad54f58
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -67,6 +67,16 @@ const bool DEBUG_OUTBOUND_MOTION =
const bool DEBUG_MODEL =
        __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Model", ANDROID_LOG_INFO);

/**
 * When multi-device input is enabled, we shouldn't use PreferStylusOverTouchBlocker at all.
 * However, multi-device input has the following default behaviour: hovering stylus rejects touch.
 * Therefore, if we want to disable that behaviour (and go back to a place where stylus down
 * blocks touch, but hovering stylus doesn't interact with touch), we should just disable the entire
 * multi-device input feature.
 */
const bool ENABLE_MULTI_DEVICE_INPUT = input_flags::enable_multi_device_input() &&
        !input_flags::disable_reject_touch_on_stylus_hover();

// Category (=namespace) name for the input settings that are applied at boot time
static const char* INPUT_NATIVE_BOOT = "input_native_boot";
/**
@@ -347,7 +357,7 @@ void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs& args) {
    ALOGD_IF(DEBUG_INBOUND_MOTION, "%s: %s", __func__, args.dump().c_str());
    { // acquire lock
        std::scoped_lock lock(mLock);
        if (input_flags::enable_multi_device_input()) {
        if (ENABLE_MULTI_DEVICE_INPUT) {
            notifyMotionLocked(args);
        } else {
            const std::vector<NotifyMotionArgs> processedArgs =
+5 −31
Original line number Diff line number Diff line
@@ -24,19 +24,6 @@

namespace android::inputdispatcher {

namespace {
bool isHoverAction(int32_t action) {
    switch (MotionEvent::getActionMasked(action)) {
        case AMOTION_EVENT_ACTION_HOVER_ENTER:
        case AMOTION_EVENT_ACTION_HOVER_MOVE:
        case AMOTION_EVENT_ACTION_HOVER_EXIT: {
            return true;
        }
    }
    return false;
}
} // namespace

InputState::InputState(const IdGenerator& idGenerator) : mIdGenerator(idGenerator) {}

InputState::~InputState() {}
@@ -113,13 +100,6 @@ bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t f
        if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties) &&
            !isStylusEvent(entry.source, entry.pointerProperties)) {
            // We already have a stylus stream, and the new event is not from stylus.
            if (!lastMemento.hovering) {
                // If stylus is currently down, reject the new event unconditionally.
                return false;
            }
        }
        if (!lastMemento.hovering && isHoverAction(action)) {
            // Reject hovers if already down
            return false;
        }
    }
@@ -366,19 +346,13 @@ bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry,
        return false;
    }

    // We want stylus down to block touch and other source types, but stylus hover should not
    // have such an effect.
    if (isHoverAction(motionEntry.action) && !lastMemento.hovering) {
        // New event is a hover. Keep the current non-hovering gesture instead
        return false;
    }

    if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties) && !lastMemento.hovering) {
        // We have non-hovering stylus already active.
    if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties)) {
        // A stylus is already active.
        if (isStylusEvent(motionEntry.source, motionEntry.pointerProperties) &&
            actionMasked == AMOTION_EVENT_ACTION_DOWN) {
            // If this new event is a stylus from a different device going down, then cancel the old
            // stylus and allow the new stylus to take over
            // If this new event is from a different device, then cancel the old
            // stylus and allow the new stylus to take over, but only if it's going down.
            // Otherwise, they will start to race each other.
            return true;
        }

+34 −38
Original line number Diff line number Diff line
@@ -2593,9 +2593,9 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusDownWithSpyBlocksTouchDown) {
/**
 * One window. Stylus hover on the window. Next, touch from another device goes down. Ensure that
 * touch is not dropped, because stylus hover should be ignored.
 * touch is dropped, because stylus hover takes precedence.
 */
TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDoesNotBlockTouchDown) {
TEST_F(InputDispatcherMultiDeviceTest, StylusHoverBlocksTouchDown) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2624,34 +2624,29 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDoesNotBlockTouchDown) {
                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
                                      .build());
    // Stylus hover is canceled because touch is down
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT),
                                     WithDeviceId(stylusDeviceId), WithCoords(100, 110)));
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId),
                                     WithCoords(140, 145)));
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
                                     WithCoords(141, 146)));
    // Touch is ignored because stylus is hovering
    // Subsequent stylus movements are ignored
    // Subsequent stylus movements are delivered correctly
    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
                                      .deviceId(stylusDeviceId)
                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
                                      .build());
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE),
                                     WithDeviceId(stylusDeviceId), WithCoords(101, 111)));
    // but subsequent touches continue to be delivered
    // and subsequent touches continue to be ignored
    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
                                      .deviceId(touchDeviceId)
                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(142).y(147))
                                      .build());
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
                                     WithCoords(142, 147)));
    window->assertNoEvents();
}
/**
 * One window. Touch down on the window. Then, stylus hover on the window from another device.
 * Ensure that touch is not canceled, because stylus hover should be dropped.
 * Ensure that touch is canceled, because stylus hover should take precedence.
 */
TEST_F(InputDispatcherMultiDeviceTest, TouchIsNotCanceledByStylusHover) {
TEST_F(InputDispatcherMultiDeviceTest, TouchIsCanceledByStylusHover) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2683,15 +2678,21 @@ TEST_F(InputDispatcherMultiDeviceTest, TouchIsNotCanceledByStylusHover) {
                                      .deviceId(stylusDeviceId)
                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
                                      .build());
    // Stylus hover movement is dropped
    // Stylus hover movement causes touch to be canceled
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId),
                                     WithCoords(141, 146)));
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
                                     WithDeviceId(stylusDeviceId), WithCoords(100, 110)));
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE),
                                     WithDeviceId(stylusDeviceId), WithCoords(101, 111)));
    // Subsequent touch movements are ignored
    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
                                      .deviceId(touchDeviceId)
                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(142).y(147))
                                      .build());
    // Subsequent touch movements are delivered correctly
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
                                     WithCoords(142, 147)));
    window->assertNoEvents();
}
/**
@@ -3008,11 +3009,11 @@ TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceWithSpy) {
 * Three windows: a window on the left, a window on the right, and a spy window positioned above
 * both.
 * Check hover in left window and touch down in the right window.
 * At first, spy should receive hover, but the touch down should cancel hovering inside spy.
 * At first, spy should receive hover. Spy shouldn't receive touch while stylus is hovering.
 * At the same time, left and right should be getting independent streams of hovering and touch,
 * respectively.
 */
TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverBlockedByTouchWithSpy) {
TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverBlocksTouchWithSpy) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> spyWindow =
@@ -3052,28 +3053,25 @@ TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverBlockedByTouchWithSpy) {
                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
                                      .build());
    leftWindow->assertNoEvents();
    spyWindow->consumeMotionEvent(
            AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
    spyWindow->consumeMotionEvent(
            AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
    spyWindow->assertNoEvents();
    rightWindow->consumeMotionEvent(
            AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
    // Stylus movements continue. They should be delivered to the left window only.
    // Stylus movements continue. They should be delivered to the left window and the spy.
    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
                                      .deviceId(stylusDeviceId)
                                      .pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
                                      .build());
    leftWindow->consumeMotionEvent(
            AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
    spyWindow->consumeMotionEvent(
            AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
    // Touch movements continue. They should be delivered to the right window and to the spy
    // Touch movements continue. They should be delivered to the right window only
    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
                                      .deviceId(touchDeviceId)
                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(301).y(101))
                                      .build());
    spyWindow->consumeMotionEvent(
            AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
    rightWindow->consumeMotionEvent(
            AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
@@ -3288,7 +3286,7 @@ TEST_F(InputDispatcherMultiDeviceTest, HoverTapAndSplitTouch) {
 * 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(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) {
TEST_F(InputDispatcherMultiDeviceTest, StylusHoverIgnoresTouchTap) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -3314,10 +3312,7 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) {
                                        .deviceId(touchDeviceId)
                                        .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
                                        .build()));
    // The touch device should cause hover to stop!
    window->consumeMotionEvent(
            AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
    // The touch device should be ignored!
    // Continue hovering with stylus.
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -3327,7 +3322,9 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) {
                                        .deviceId(stylusDeviceId)
                                        .pointer(PointerBuilder(0, ToolType::STYLUS).x(60).y(60))
                                        .build()));
    // Hovers are now ignored
    // Hovers continue to work
    window->consumeMotionEvent(
            AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
    // Lift up the finger
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -3337,7 +3334,6 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) {
                                        .deviceId(touchDeviceId)
                                        .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
                                        .build()));
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(touchDeviceId)));
    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
              injectMotionEvent(*mDispatcher,
@@ -3346,8 +3342,8 @@ TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDroppedWhenTouchTap) {
                                        .deviceId(stylusDeviceId)
                                        .pointer(PointerBuilder(0, ToolType::STYLUS).x(70).y(70))
                                        .build()));
    window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
                                     WithDeviceId(stylusDeviceId)));
    window->consumeMotionEvent(
            AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
    window->assertNoEvents();
}