Loading services/inputflinger/dispatcher/CancelationOptions.h +1 −0 Original line number Diff line number Diff line Loading @@ -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. Loading services/inputflinger/dispatcher/InputDispatcher.cpp +5 −4 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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); Loading services/inputflinger/tests/InputDispatcher_test.cpp +80 −0 Original line number Diff line number Diff line Loading @@ -2460,6 +2460,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. Loading Loading
services/inputflinger/dispatcher/CancelationOptions.h +1 −0 Original line number Diff line number Diff line Loading @@ -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. Loading
services/inputflinger/dispatcher/InputDispatcher.cpp +5 −4 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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); Loading
services/inputflinger/tests/InputDispatcher_test.cpp +80 −0 Original line number Diff line number Diff line Loading @@ -2460,6 +2460,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. Loading