Remove TouchState::split variable

Instead of storing this state and mutating it in various places, let's
replace the variable with some logic about whether a touch should be
split.

Ideally, we wouldn't even have custom logic for split vs non-split, but
this is a starting point before we can refactor the code further.

The short-term goal is to simplify the findTouchedWindowTargetsLocked
function.

Bug: 211379801
Test: atest inputflinger_tests
Change-Id: I799c2e11a72052a2867710593b18e05011ca44b6
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index ead093c..63a2418 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2034,6 +2034,36 @@
     return responsiveMonitors;
 }
 
+/**
+ * In general, touch should be always split between windows. Some exceptions:
+ * 1. Don't split touch is if we have an active pointer down, and a new pointer is going down that's
+ * from the same device, *and* the window that's receiving the current pointer does not support
+ * split touch.
+ * 2. Don't split mouse events
+ */
+bool InputDispatcher::shouldSplitTouch(const TouchState& touchState,
+                                       const MotionEntry& entry) const {
+    if (isFromSource(entry.source, AINPUT_SOURCE_MOUSE)) {
+        // We should never split mouse events
+        return false;
+    }
+    for (const TouchedWindow& touchedWindow : touchState.windows) {
+        if (touchedWindow.windowHandle->getInfo()->isSpy()) {
+            // Spy windows should not affect whether or not touch is split.
+            continue;
+        }
+        if (touchedWindow.windowHandle->getInfo()->supportsSplitTouch()) {
+            continue;
+        }
+        // Eventually, touchedWindow will contain the deviceId of each pointer that's currently
+        // being sent there. For now, use deviceId from touch state.
+        if (entry.deviceId == touchState.deviceId && !touchedWindow.pointerIds.isEmpty()) {
+            return false;
+        }
+    }
+    return true;
+}
+
 std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(
         nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
         InputEventInjectionResult& outInjectionResult) {
@@ -2061,7 +2091,7 @@
         tempTouchState = *oldState;
     }
 
-    bool isSplit = tempTouchState.split;
+    bool isSplit = shouldSplitTouch(tempTouchState, entry);
     bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 &&
             (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source ||
              tempTouchState.displayId != displayId);
@@ -2141,7 +2171,7 @@
             // No window is touched, so set split to true. This will allow the next pointer down to
             // be delivered to a new window which supports split touch. Pointers from a mouse device
             // should never be split.
-            tempTouchState.split = isSplit = !isFromMouse;
+            isSplit = !isFromMouse;
         }
 
         // Update hover state.
@@ -5301,9 +5331,9 @@
         dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
         for (const std::pair<int32_t, TouchState>& pair : mTouchStatesByDisplay) {
             const TouchState& state = pair.second;
-            dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n",
-                                 state.displayId, toString(state.down), toString(state.split),
-                                 state.deviceId, state.source);
+            dump += StringPrintf(INDENT2 "%d: down=%s, deviceId=%d, source=0x%08x\n",
+                                 state.displayId, toString(state.down), state.deviceId,
+                                 state.source);
             if (!state.windows.empty()) {
                 dump += INDENT3 "Windows:\n";
                 for (size_t i = 0; i < state.windows.size(); i++) {
@@ -5676,10 +5706,7 @@
                                "input channel stole pointer stream");
     options.deviceId = state.deviceId;
     options.displayId = state.displayId;
-    if (state.split) {
-        // If split pointers then selectively cancel pointers otherwise cancel all pointers
-        options.pointerIds = window.pointerIds;
-    }
+    options.pointerIds = window.pointerIds;
     std::string canceledWindows;
     for (const TouchedWindow& w : state.windows) {
         const std::shared_ptr<InputChannel> channel =
@@ -5698,11 +5725,7 @@
     // This only blocks relevant pointers to be sent to other windows
     window.isPilferingPointers = true;
 
-    if (state.split) {
-        state.cancelPointersForWindowsExcept(window.pointerIds, token);
-    } else {
-        state.filterWindowsExcept(token);
-    }
+    state.cancelPointersForWindowsExcept(window.pointerIds, token);
     return OK;
 }
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index c2fe196..3b44a8e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -541,6 +541,7 @@
     // shade is pulled down while we are counting down the timeout).
     void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
 
+    bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) const;
     int32_t getTargetDisplayId(const EventEntry& entry);
     sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked(
             nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index cf0c38a..eb31046 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -31,10 +31,6 @@
 
 void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int32_t targetFlags,
                                    BitSet32 pointerIds, std::optional<nsecs_t> eventTime) {
-    if (targetFlags & InputTarget::FLAG_SPLIT) {
-        split = true;
-    }
-
     for (size_t i = 0; i < windows.size(); i++) {
         TouchedWindow& touchedWindow = windows[i];
         if (touchedWindow.windowHandle == windowHandle) {
@@ -105,11 +101,6 @@
     std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.isEmpty(); });
 }
 
-void TouchState::filterWindowsExcept(const sp<IBinder>& token) {
-    std::erase_if(windows,
-                  [&token](const TouchedWindow& w) { return w.windowHandle->getToken() != token; });
-}
-
 sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
     for (size_t i = 0; i < windows.size(); i++) {
         const TouchedWindow& window = windows[i];
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index cf5f1e5..d324611 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -29,7 +29,6 @@
 
 struct TouchState {
     bool down = false;
-    bool split = false;
 
     // id of the device that is currently down, others are rejected
     int32_t deviceId = -1;
@@ -50,7 +49,6 @@
                            std::optional<nsecs_t> eventTime = std::nullopt);
     void removeWindowByToken(const sp<IBinder>& token);
     void filterNonAsIsTouchWindows();
-    void filterWindowsExcept(const sp<IBinder>& token);
 
     // Cancel pointers for current set of windows except the window with particular binder token.
     void cancelPointersForWindowsExcept(const BitSet32 pointerIds, const sp<IBinder>& token);