FocusResolver: Clean up focus requests and results when a display is removed

Prevents FocusResolver from leaking focus requests and results as
displays are added and removed.

Test: atest inputflinger_tests
Fixes: 188008394
Change-Id: I635b9becf91056fd540ab9d9546dd67a09324fc6
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index d2b8739..c0010ab 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4613,30 +4613,34 @@
     }
     { // acquire lock
         std::scoped_lock _l(mLock);
-
-        std::shared_ptr<InputApplicationHandle> oldFocusedApplicationHandle =
-                getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
-
-        if (sharedPointersEqual(oldFocusedApplicationHandle, inputApplicationHandle)) {
-            return; // This application is already focused. No need to wake up or change anything.
-        }
-
-        // Set the new application handle.
-        if (inputApplicationHandle != nullptr) {
-            mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
-        } else {
-            mFocusedApplicationHandlesByDisplay.erase(displayId);
-        }
-
-        // No matter what the old focused application was, stop waiting on it because it is
-        // no longer focused.
-        resetNoFocusedWindowTimeoutLocked();
+        setFocusedApplicationLocked(displayId, inputApplicationHandle);
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
     mLooper->wake();
 }
 
+void InputDispatcher::setFocusedApplicationLocked(
+        int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
+    std::shared_ptr<InputApplicationHandle> oldFocusedApplicationHandle =
+            getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
+
+    if (sharedPointersEqual(oldFocusedApplicationHandle, inputApplicationHandle)) {
+        return; // This application is already focused. No need to wake up or change anything.
+    }
+
+    // Set the new application handle.
+    if (inputApplicationHandle != nullptr) {
+        mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
+    } else {
+        mFocusedApplicationHandlesByDisplay.erase(displayId);
+    }
+
+    // No matter what the old focused application was, stop waiting on it because it is
+    // no longer focused.
+    resetNoFocusedWindowTimeoutLocked();
+}
+
 /**
  * Sets the focused display, which is responsible for receiving focus-dispatched input events where
  * the display not specified.
@@ -6208,4 +6212,19 @@
     mLock.lock();
 }
 
+void InputDispatcher::displayRemoved(int32_t displayId) {
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+        // Set an empty list to remove all handles from the specific display.
+        setInputWindowsLocked(/* window handles */ {}, displayId);
+        setFocusedApplicationLocked(displayId, nullptr);
+        // Call focus resolver to clean up stale requests. This must be called after input windows
+        // have been removed for the removed display.
+        mFocusResolver.displayRemoved(displayId);
+    } // release lock
+
+    // Wake up poll loop since it may need to make new input dispatching choices.
+    mLooper->wake();
+}
+
 } // namespace android::inputdispatcher