Allow spy windows to prevent splitting
To avoid a crash in dispatcher, allow spy window to prevent splitting.
This would get the behaviour more in line with the existing app windows.
In the future, we need to holistically revisit the splitting behaviour.
The crash that this fixes can happen as follows:
In freeform window scenario, enable outbound event verification and
repeatedly touch the corners of the freeform window while
attempting to resize.
The spy windows are being used for capturing the window resizing
gesture, and also for interaction with the window's caption bar for
repositioning.
The test in this patch reproduces this scenario.
In this fix, we add a splitting behaviour to spy window to roughly match
the behaviour of the regular windows - if a first pointer lands into a
window that prevents splitting, then the remaining pointers will
continue going to that window, even if they are outside. This behaviour
is not very clear in various window configurations where one window
supports splitting while another doesn't. This will be revisited in
subsequent patches. The goal with this CL is to just fix the crash.
Bug: 347700797
Flag: EXEMPT bugfix
Test: TEST=inputflinger_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST --gtest_break_on_failure --gtest_filter="*SpyThatPreventsSplittingWithApplication*"
Change-Id: I7d7c8668e993e66da0e39d1da8bb81831f6b3cc9
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 7b974a8..6c4870f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1308,7 +1308,7 @@
// Alternatively, maybe there's a spy window that could handle this event.
const std::vector<sp<WindowInfoHandle>> touchedSpies =
- findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus);
+ findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus, motionEntry.deviceId);
for (const auto& windowHandle : touchedSpies) {
const std::shared_ptr<Connection> connection =
getConnectionLocked(windowHandle->getToken());
@@ -1463,15 +1463,27 @@
}
std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
- ui::LogicalDisplayId displayId, float x, float y, bool isStylus) const {
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus, DeviceId deviceId) const {
// Traverse windows from front to back and gather the touched spy windows.
std::vector<sp<WindowInfoHandle>> spyWindows;
const auto& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
const WindowInfo& info = *windowHandle->getInfo();
-
if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
- continue;
+ // Generally, we would skip any pointer that's outside of the window. However, if the
+ // spy prevents splitting, and already has some of the pointers from this device, then
+ // it should get more pointers from the same device, even if they are outside of that
+ // window
+ if (info.supportsSplitTouch()) {
+ continue;
+ }
+
+ // We know that split touch is not supported. Skip this window only if it doesn't have
+ // any touching pointers for this device already.
+ if (!windowHasTouchingPointersLocked(windowHandle, deviceId)) {
+ continue;
+ }
+ // If it already has pointers down for this device, then give it this pointer, too.
}
if (!info.isSpy()) {
// The first touched non-spy window was found, so return the spy windows touched so far.
@@ -2481,7 +2493,7 @@
}
std::vector<sp<WindowInfoHandle>> newTouchedWindows =
- findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus);
+ findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus, entry.deviceId);
if (newTouchedWindowHandle != nullptr) {
// Process the foreground window first so that it is the first to receive the event.
newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
@@ -5670,7 +5682,7 @@
mMaximumObscuringOpacityForTouch = opacity;
}
-std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId /*displayId*/>
+std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId>
InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) {
for (auto& [displayId, state] : mTouchStatesByDisplay) {
for (TouchedWindow& w : state.windows) {
@@ -5682,6 +5694,22 @@
return std::make_tuple(nullptr, nullptr, ui::LogicalDisplayId::DEFAULT);
}
+std::tuple<const TouchState*, const TouchedWindow*, ui::LogicalDisplayId>
+InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) const {
+ return const_cast<InputDispatcher*>(this)->findTouchStateWindowAndDisplayLocked(token);
+}
+
+bool InputDispatcher::windowHasTouchingPointersLocked(const sp<WindowInfoHandle>& windowHandle,
+ DeviceId deviceId) const {
+ const auto& [touchState, touchedWindow, _] =
+ findTouchStateWindowAndDisplayLocked(windowHandle->getToken());
+ if (touchState == nullptr) {
+ // No touching pointers at all
+ return false;
+ }
+ return touchState->hasTouchingPointers(deviceId);
+}
+
bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
bool isDragDrop) {
if (fromToken == toToken) {