InputDispatcher: Do not drop ongoing gesture if a window is no longer a spy
The old InputDispatcher policy was that for all touch events, there had
to be at least one touched foreground window or spy window. If not, we
dropped the event.
This caused events to be dropped if a handwriting interceptor spy window
changed its configuration mid-gesture to no longer be a spy. To get
around this, we define non-touchable and spy windows as windows that
"cannot be foreground", and require at least one touched foreground
window or at least one touched window that cannot be foreground.
Bug: 221908983
Test: atest inputflinger_tests
Test: manual: ensure Scribe works
Change-Id: If5fcbf6f4f784184377434f71f1ac121517b023a
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 6bfac6c..c8a3ccf 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -556,6 +556,17 @@
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 ---
@@ -2203,8 +2214,8 @@
// 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;
}
@@ -2277,8 +2288,10 @@
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;
}
@@ -2327,13 +2340,15 @@
}
}
- // 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);
@@ -5072,9 +5087,11 @@
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.