Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b13da8fa authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

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
parent afb7d61d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -4002,7 +4002,7 @@ void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(

void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
        const std::shared_ptr<Connection>& connection, const CancelationOptions& options) {
    if (connection->status == Connection::Status::BROKEN) {
    if (connection->status != Connection::Status::NORMAL) {
        return;
    }

+33 −0
Original line number Diff line number Diff line
@@ -6974,6 +6974,39 @@ TEST_F(InputDispatcherFallbackKeyTest, CanceledKeyCancelsFallback) {
    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;