Loading services/inputflinger/dispatcher/InputDispatcher.cpp +29 −12 Original line number Diff line number Diff line Loading @@ -556,6 +556,17 @@ bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) { entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER); } // Determines if the given window can be targeted as InputTarget::FLAG_FOREGROUND. // Foreground events are only sent to "foreground targetable" windows, but not all gestures sent to // such window are necessarily targeted with the flag. For example, an event with ACTION_OUTSIDE can // be sent to such a window, but it is not a foreground event and doesn't use // InputTarget::FLAG_FOREGROUND. bool canReceiveForegroundTouches(const WindowInfo& info) { // A non-touchable window can still receive touch events (e.g. in the case of // STYLUS_INTERCEPTOR), so prevent such windows from receiving foreground events for touches. return !info.inputConfig.test(gui::WindowInfo::InputConfig::NOT_TOUCHABLE) && !info.isSpy(); } } // namespace // --- InputDispatcher --- Loading Loading @@ -2203,8 +2214,8 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // Set target flags. int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_IS; if (!info.isSpy()) { // There should only be one new foreground (non-spy) window at this location. if (canReceiveForegroundTouches(*windowHandle->getInfo())) { // There should only be one touched window that can be "foreground" for the pointer. targetFlags |= InputTarget::FLAG_FOREGROUND; } Loading Loading @@ -2277,8 +2288,10 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( isSplit = !isFromMouse; } int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER; int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER; if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) { targetFlags |= InputTarget::FLAG_FOREGROUND; } if (isSplit) { targetFlags |= InputTarget::FLAG_SPLIT; } Loading Loading @@ -2327,13 +2340,15 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( } } // Ensure that we have at least one foreground or spy window. It's possible that we dropped some // of the touched windows we previously found if they became paused or unresponsive or were // removed. // Ensure that we have at least one foreground window or at least one window that cannot be a // foreground target. If we only have windows that are not receiving foreground touches (e.g. we // only have windows getting ACTION_OUTSIDE), then drop the event, because there is no window // that is actually receiving the entire gesture. if (std::none_of(tempTouchState.windows.begin(), tempTouchState.windows.end(), [](const TouchedWindow& touchedWindow) { return (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0 || touchedWindow.windowHandle->getInfo()->isSpy(); return !canReceiveForegroundTouches( *touchedWindow.windowHandle->getInfo()) || (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0; })) { ALOGI("Dropping event because there is no touched window on display %d to receive it.", displayId); Loading Loading @@ -5072,9 +5087,11 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< state->removeWindowByToken(fromToken); // Add new window. int32_t newTargetFlags = oldTargetFlags & (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS); int32_t newTargetFlags = oldTargetFlags & (InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS); if (canReceiveForegroundTouches(*toWindowHandle->getInfo())) { newTargetFlags |= InputTarget::FLAG_FOREGROUND; } state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds); // Store the dragging window. Loading services/inputflinger/tests/InputDispatcher_test.cpp +34 −0 Original line number Diff line number Diff line Loading @@ -6915,4 +6915,38 @@ TEST_F(InputDispatcherStylusInterceptorTest, SpyWindowStylusInterceptor) { window->assertNoEvents(); } /** * Set up a scenario to test the behavior used by the stylus handwriting detection feature. * The scenario is as follows: * - The stylus interceptor overlay is configured as a spy window. * - The stylus interceptor spy receives the start of a new stylus gesture. * - It pilfers pointers and then configures itself to no longer be a spy. * - The stylus interceptor continues to receive the rest of the gesture. */ TEST_F(InputDispatcherStylusInterceptorTest, StylusHandwritingScenario) { auto [overlay, window] = setupStylusOverlayScenario(); overlay->setSpy(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}); sendStylusEvent(AMOTION_EVENT_ACTION_DOWN); overlay->consumeMotionDown(); window->consumeMotionDown(); // The interceptor pilfers the pointers. EXPECT_EQ(OK, mDispatcher->pilferPointers(overlay->getToken())); window->consumeMotionCancel(); // The interceptor configures itself so that it is no longer a spy. overlay->setSpy(false); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}); // It continues to receive the rest of the stylus gesture. sendStylusEvent(AMOTION_EVENT_ACTION_MOVE); overlay->consumeMotionMove(); sendStylusEvent(AMOTION_EVENT_ACTION_UP); overlay->consumeMotionUp(); window->assertNoEvents(); } } // namespace android::inputdispatcher Loading
services/inputflinger/dispatcher/InputDispatcher.cpp +29 −12 Original line number Diff line number Diff line Loading @@ -556,6 +556,17 @@ bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) { entry.pointerProperties[pointerIndex].toolType == AMOTION_EVENT_TOOL_TYPE_ERASER); } // Determines if the given window can be targeted as InputTarget::FLAG_FOREGROUND. // Foreground events are only sent to "foreground targetable" windows, but not all gestures sent to // such window are necessarily targeted with the flag. For example, an event with ACTION_OUTSIDE can // be sent to such a window, but it is not a foreground event and doesn't use // InputTarget::FLAG_FOREGROUND. bool canReceiveForegroundTouches(const WindowInfo& info) { // A non-touchable window can still receive touch events (e.g. in the case of // STYLUS_INTERCEPTOR), so prevent such windows from receiving foreground events for touches. return !info.inputConfig.test(gui::WindowInfo::InputConfig::NOT_TOUCHABLE) && !info.isSpy(); } } // namespace // --- InputDispatcher --- Loading Loading @@ -2203,8 +2214,8 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // Set target flags. int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_IS; if (!info.isSpy()) { // There should only be one new foreground (non-spy) window at this location. if (canReceiveForegroundTouches(*windowHandle->getInfo())) { // There should only be one touched window that can be "foreground" for the pointer. targetFlags |= InputTarget::FLAG_FOREGROUND; } Loading Loading @@ -2277,8 +2288,10 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( isSplit = !isFromMouse; } int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER; int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER; if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) { targetFlags |= InputTarget::FLAG_FOREGROUND; } if (isSplit) { targetFlags |= InputTarget::FLAG_SPLIT; } Loading Loading @@ -2327,13 +2340,15 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( } } // Ensure that we have at least one foreground or spy window. It's possible that we dropped some // of the touched windows we previously found if they became paused or unresponsive or were // removed. // Ensure that we have at least one foreground window or at least one window that cannot be a // foreground target. If we only have windows that are not receiving foreground touches (e.g. we // only have windows getting ACTION_OUTSIDE), then drop the event, because there is no window // that is actually receiving the entire gesture. if (std::none_of(tempTouchState.windows.begin(), tempTouchState.windows.end(), [](const TouchedWindow& touchedWindow) { return (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0 || touchedWindow.windowHandle->getInfo()->isSpy(); return !canReceiveForegroundTouches( *touchedWindow.windowHandle->getInfo()) || (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0; })) { ALOGI("Dropping event because there is no touched window on display %d to receive it.", displayId); Loading Loading @@ -5072,9 +5087,11 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< state->removeWindowByToken(fromToken); // Add new window. int32_t newTargetFlags = oldTargetFlags & (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS); int32_t newTargetFlags = oldTargetFlags & (InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS); if (canReceiveForegroundTouches(*toWindowHandle->getInfo())) { newTargetFlags |= InputTarget::FLAG_FOREGROUND; } state->addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds); // Store the dragging window. Loading
services/inputflinger/tests/InputDispatcher_test.cpp +34 −0 Original line number Diff line number Diff line Loading @@ -6915,4 +6915,38 @@ TEST_F(InputDispatcherStylusInterceptorTest, SpyWindowStylusInterceptor) { window->assertNoEvents(); } /** * Set up a scenario to test the behavior used by the stylus handwriting detection feature. * The scenario is as follows: * - The stylus interceptor overlay is configured as a spy window. * - The stylus interceptor spy receives the start of a new stylus gesture. * - It pilfers pointers and then configures itself to no longer be a spy. * - The stylus interceptor continues to receive the rest of the gesture. */ TEST_F(InputDispatcherStylusInterceptorTest, StylusHandwritingScenario) { auto [overlay, window] = setupStylusOverlayScenario(); overlay->setSpy(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}); sendStylusEvent(AMOTION_EVENT_ACTION_DOWN); overlay->consumeMotionDown(); window->consumeMotionDown(); // The interceptor pilfers the pointers. EXPECT_EQ(OK, mDispatcher->pilferPointers(overlay->getToken())); window->consumeMotionCancel(); // The interceptor configures itself so that it is no longer a spy. overlay->setSpy(false); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {overlay, window}}}); // It continues to receive the rest of the stylus gesture. sendStylusEvent(AMOTION_EVENT_ACTION_MOVE); overlay->consumeMotionMove(); sendStylusEvent(AMOTION_EVENT_ACTION_UP); overlay->consumeMotionUp(); window->assertNoEvents(); } } // namespace android::inputdispatcher