InputDispatcher: Do not synthesize cancelations for zombie connections

When an input channel is removed, we mark it as a zombie connection in
Dispatcher.

Whenever we leave a critical section inside Dispatcher, it is possible
for an input channel to be removed. When handling fallback keys, we make
a policy call synchronously while processing the unhandled key
by releasing the lock. After returning from the policy call, it's
possible the input channel was removed, leaving the connection in a
zombie state.

When in this state, if we try to synthesize a cancelation event, we
would end up crashing. To avoid this, ensure we don't try to synthesize
cancelations for channels that aren't normal (i.e. broken or zombie
channels).

Bug: 319186639
Test: atest inputflinger_tests
Change-Id: I05a9b001de19bf26d81ea50a511d84bc3a2045d2
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 64f2372..6abaa55 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -6974,6 +6974,39 @@
     mWindow->assertNoEvents();
 }
 
+TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedDuringPolicyCall) {
+    setFallback(AKEYCODE_B);
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+    // Do not handle this key event.
+    consumeKey(/*handled=*/false,
+               AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+    consumeKey(/*handled=*/true,
+               AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+                     WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+    mFakePolicy->setUnhandledKeyHandler([&](const KeyEvent& event) {
+        // When the unhandled key is reported to the policy next, remove the input channel.
+        mDispatcher->removeInputChannel(mWindow->getToken());
+        return KeyEventBuilder(event).keyCode(AKEYCODE_B).build();
+    });
+    // Release the original key, and let the app now handle the previously unhandled key.
+    // This should result in the previously generated fallback key to be cancelled.
+    // Since the policy was notified of the unhandled DOWN event earlier, it will also be notified
+    // of the UP event for consistency. The Dispatcher calls into the policy from its own thread
+    // without holding the lock, because it need to synchronously fetch the fallback key. While in
+    // the policy call, we will now remove the input channel. Once the policy call returns, the
+    // Dispatcher will no longer have a channel to send cancellation events to. Ensure this does
+    // not cause any crashes.
+    mDispatcher->notifyKey(
+            KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+    consumeKey(/*handled=*/true,
+               AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+}
+
 class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
 protected:
     static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms;