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/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index 17efb5b..9051ff1 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -256,5 +256,37 @@
     // dropped.
     ASSERT_FALSE(changes);
 }
+TEST(FocusResolverTest, FocusRequestsAreClearedWhenWindowIsRemoved) {
+    sp<IBinder> windowToken = new BBinder();
+    std::vector<sp<InputWindowHandle>> windows;
+
+    sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken,
+                                                       true /* focusable */, true /* visible */);
+    windows.push_back(window);
+
+    FocusRequest request;
+    request.displayId = 42;
+    request.token = windowToken;
+    FocusResolver focusResolver;
+    std::optional<FocusResolver::FocusChanges> changes =
+            focusResolver.setFocusedWindow(request, windows);
+    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+    ASSERT_EQ(request.displayId, changes->displayId);
+
+    // Start with a focused window
+    window->setFocusable(true);
+    changes = focusResolver.setInputWindows(request.displayId, windows);
+    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+
+    // When a display is removed, all windows are removed from the display
+    // and our focused window loses focus
+    changes = focusResolver.setInputWindows(request.displayId, {});
+    ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
+    focusResolver.displayRemoved(request.displayId);
+
+    // When a display is readded, the window does not get focus since the request was cleared.
+    changes = focusResolver.setInputWindows(request.displayId, windows);
+    ASSERT_FALSE(changes);
+}
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index d51acce..77ca12c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -2688,6 +2688,23 @@
     window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
 }
 
+TEST_F(InputDispatcherTest, DisplayRemoved) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "window", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    // window is granted focus.
+    window->setFocusable(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
+    window->consumeFocusEvent(true);
+
+    // When a display is removed window loses focus.
+    mDispatcher->displayRemoved(ADISPLAY_ID_DEFAULT);
+    window->consumeFocusEvent(false);
+}
+
 /**
  * Launch two windows, with different owners. One window (slipperyExitWindow) has Flag::SLIPPERY,
  * and overlaps the other window, slipperyEnterWindow. The window 'slipperyExitWindow' is on top