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;