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

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

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
parent 203b22aa
Loading
Loading
Loading
Loading
+16 −6
Original line number Diff line number Diff line
@@ -2089,13 +2089,23 @@ void InputDispatcher::cancelEventsForAnrLocked(const std::shared_ptr<Connection>
    // pile up.
    ALOGW("Canceling events for %s because it is unresponsive",
          connection->getInputChannelName().c_str());
    if (connection->status == Connection::Status::NORMAL) {
    if (connection->status != Connection::Status::NORMAL) {
        return;
    }
    CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS,
                               "application not responding");
        synthesizeCancelationEventsForConnectionLocked(connection, options,
                                                       getWindowHandleLocked(
                                                               connection->getToken()));

    sp<WindowInfoHandle> windowHandle;
    if (!connection->monitor) {
        windowHandle = getWindowHandleLocked(connection->getToken());
        if (windowHandle == nullptr) {
            // The window that is receiving this ANR was removed, so there is no need to generate
            // cancellations, because the cancellations would have already been generated when
            // the window was removed.
            return;
        }
    }
    synthesizeCancelationEventsForConnectionLocked(connection, options, windowHandle);
}

void InputDispatcher::resetNoFocusedWindowTimeoutLocked() {
+60 −7
Original line number Diff line number Diff line
@@ -155,7 +155,7 @@ static KeyEvent getTestKeyEvent() {
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 @@ public:
    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 @@ public:
    }
    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 @@ private:
    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 @@ TEST_F(InputDispatcherSingleWindowAnr, TwoGesturesWithAnr) {
    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();