InputDispatcher: Fix crash when there is an ANR after window removal

This addresses a bug in the change:
I47744cbd677cc74e26a102c50a2c11c68bc8aa89

InputDispatcher has an invariant that it can only send events to a
connection if it has a window. We did not check if the channel receiving
an ANR had a window before attempting to synthesize cancellations for
it.

Bug: 324330557
Bug: 210460522
Test: atest inputflinger_tests
Change-Id: I5f3013fe93c0f4d1bb0f58e7b2a241cffe5c5bf2
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 1c37da0..31c7c67 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -155,7 +155,7 @@
 class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
     struct AnrResult {
         sp<IBinder> token{};
-        gui::Pid pid{gui::Pid::INVALID};
+        std::optional<gui::Pid> pid{};
     };
     /* Stores data about a user-activity-poke event from the dispatcher. */
     struct UserActivityPokeEvent {
@@ -260,7 +260,7 @@
 
     void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
                                                  const sp<IBinder>& expectedToken,
-                                                 gui::Pid expectedPid) {
+                                                 std::optional<gui::Pid> expectedPid) {
         std::unique_lock lock(mLock);
         android::base::ScopedLockAssertion assumeLocked(mLock);
         AnrResult result;
@@ -280,7 +280,7 @@
     }
 
     void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedToken,
-                                               gui::Pid expectedPid) {
+                                               std::optional<gui::Pid> expectedPid) {
         std::unique_lock lock(mLock);
         android::base::ScopedLockAssertion assumeLocked(mLock);
         AnrResult result;
@@ -524,16 +524,14 @@
     void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid,
                                   const std::string&) override {
         std::scoped_lock lock(mLock);
-        ASSERT_TRUE(pid.has_value());
-        mAnrWindows.push({connectionToken, *pid});
+        mAnrWindows.push({connectionToken, pid});
         mNotifyAnr.notify_all();
     }
 
     void notifyWindowResponsive(const sp<IBinder>& connectionToken,
                                 std::optional<gui::Pid> pid) override {
         std::scoped_lock lock(mLock);
-        ASSERT_TRUE(pid.has_value());
-        mResponsiveWindows.push({connectionToken, *pid});
+        mResponsiveWindows.push({connectionToken, pid});
         mNotifyAnr.notify_all();
     }
 
@@ -9059,6 +9057,61 @@
     mWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
 }
 
+// Send an event to the app and have the app not respond right away. Then remove the app window.
+// When the window is removed, the dispatcher will cancel the events for that window.
+// So InputDispatcher will enqueue ACTION_CANCEL event as well.
+TEST_F(InputDispatcherSingleWindowAnr, AnrAfterWindowRemoval) {
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {WINDOW_LOCATION}));
+
+    const auto [sequenceNum, _] = mWindow->receiveEvent(); // ACTION_DOWN
+    ASSERT_TRUE(sequenceNum);
+
+    // Remove the window, but the input channel should remain alive.
+    mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
+
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    // Since the window was removed, Dispatcher does not know the PID associated with the window
+    // anymore, so the policy is notified without the PID.
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken(),
+                                                         /*pid=*/std::nullopt);
+
+    mWindow->finishEvent(*sequenceNum);
+    // The cancellation was generated when the window was removed, along with the focus event.
+    mWindow->consumeMotionEvent(
+            AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+    mWindow->consumeFocusEvent(false);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), /*pid=*/std::nullopt);
+}
+
+// Send an event to the app and have the app not respond right away. Wait for the policy to be
+// notified of the unresponsive window, then remove the app window.
+TEST_F(InputDispatcherSingleWindowAnr, AnrFollowedByWindowRemoval) {
+    mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+                                                 AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                                 {WINDOW_LOCATION}));
+
+    const auto [sequenceNum, _] = mWindow->receiveEvent(); // ACTION_DOWN
+    ASSERT_TRUE(sequenceNum);
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
+
+    // Remove the window, but the input channel should remain alive.
+    mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
+
+    mWindow->finishEvent(*sequenceNum);
+    // The cancellation was generated during the ANR, and the window lost focus when it was removed.
+    mWindow->consumeMotionEvent(
+            AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+    mWindow->consumeFocusEvent(false);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    // Since the window was removed, Dispatcher does not know the PID associated with the window
+    // becoming responsive, so the policy is notified without the PID.
+    mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), /*pid=*/std::nullopt);
+}
+
 class InputDispatcherMultiWindowAnr : public InputDispatcherTest {
     virtual void SetUp() override {
         InputDispatcherTest::SetUp();