Tell policy about responsive connections

If a connection is unresponsive, we tell the policy about it. Now, with
this patch, we will also tell the policy that a connection becomes
responsive again. This will allow the policy to cancel the ANR dialog if
it's currently shown, and to stop recurring ANR dialogs if the user was
previously waiting for the app longer.

Bug: 171218828
Test: atest inputflinger_tests
Test: manual test via unresponsive apps
Change-Id: Ifc00601b2c4e44b721a30d5f9ebf786780d1ffd7
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index c2d165e..9fea298 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -50,12 +50,18 @@
 private:
     void notifyConfigurationChanged(nsecs_t) override {}
 
-    std::chrono::nanoseconds notifyAnr(const std::shared_ptr<InputApplicationHandle>&,
-                                       const sp<IBinder>&, const std::string& name) override {
-        ALOGE("The window is not responding : %s", name.c_str());
-        return 0s;
+    void notifyNoFocusedWindowAnr(
+            const std::shared_ptr<InputApplicationHandle>& applicationHandle) override {
+        ALOGE("There is no focused window for %s", applicationHandle->getName().c_str());
     }
 
+    void notifyConnectionUnresponsive(const sp<IBinder>& connectionToken,
+                                      const std::string& reason) override {
+        ALOGE("Connection is not responding: %s", reason.c_str());
+    }
+
+    void notifyConnectionResponsive(const sp<IBinder>& connectionToken) override {}
+
     void notifyInputChannelBroken(const sp<IBinder>&) override {}
 
     void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index d6fa74d..bb27469 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2335,7 +2335,7 @@
 }
 
 std::string InputDispatcher::getApplicationWindowLabel(
-        const std::shared_ptr<InputApplicationHandle>& applicationHandle,
+        const InputApplicationHandle* applicationHandle,
         const sp<InputWindowHandle>& windowHandle) {
     if (applicationHandle != nullptr) {
         if (windowHandle != nullptr) {
@@ -4921,24 +4921,21 @@
     sp<IBinder> connectionToken = connection.inputChannel->getConnectionToken();
     updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);
 
-    std::unique_ptr<CommandEntry> commandEntry =
-            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
-    commandEntry->inputApplicationHandle = nullptr;
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doNotifyConnectionUnresponsiveLockedInterruptible);
     commandEntry->connectionToken = connectionToken;
     commandEntry->reason = std::move(reason);
     postCommandLocked(std::move(commandEntry));
 }
 
-void InputDispatcher::onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) {
-    std::string reason = android::base::StringPrintf("%s does not have a focused window",
-                                                     application->getName().c_str());
+void InputDispatcher::onAnrLocked(std::shared_ptr<InputApplicationHandle> application) {
+    std::string reason =
+            StringPrintf("%s does not have a focused window", application->getName().c_str());
+    updateLastAnrStateLocked(*application, reason);
 
-    updateLastAnrStateLocked(application, reason);
-
-    std::unique_ptr<CommandEntry> commandEntry =
-            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
-    commandEntry->inputApplicationHandle = application;
-    commandEntry->reason = std::move(reason);
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible);
+    commandEntry->inputApplicationHandle = std::move(application);
     postCommandLocked(std::move(commandEntry));
 }
 
@@ -4955,9 +4952,9 @@
     updateLastAnrStateLocked(windowLabel, reason);
 }
 
-void InputDispatcher::updateLastAnrStateLocked(
-        const std::shared_ptr<InputApplicationHandle>& application, const std::string& reason) {
-    const std::string windowLabel = getApplicationWindowLabel(application, nullptr);
+void InputDispatcher::updateLastAnrStateLocked(const InputApplicationHandle& application,
+                                               const std::string& reason) {
+    const std::string windowLabel = getApplicationWindowLabel(&application, nullptr);
     updateLastAnrStateLocked(windowLabel, reason);
 }
 
@@ -5005,24 +5002,36 @@
     mLock.lock();
 }
 
-void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) {
     mLock.unlock();
-    const sp<IBinder>& token = commandEntry->connectionToken;
-    const std::chrono::nanoseconds timeoutExtension =
-            mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
+
+    mPolicy->notifyNoFocusedWindowAnr(commandEntry->inputApplicationHandle);
+
+    mLock.lock();
+}
+
+void InputDispatcher::doNotifyConnectionUnresponsiveLockedInterruptible(
+        CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    mPolicy->notifyConnectionUnresponsive(commandEntry->connectionToken, commandEntry->reason);
 
     mLock.lock();
 
-    if (timeoutExtension > 0s) {
-        extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension);
-    } else {
-        // stop waking up for events in this connection, it is already not responding
-        sp<Connection> connection = getConnectionLocked(token);
-        if (connection == nullptr) {
-            return;
-        }
-        cancelEventsForAnrLocked(connection);
+    // stop waking up for events in this connection, it is already not responding
+    sp<Connection> connection = getConnectionLocked(commandEntry->connectionToken);
+    if (connection == nullptr) {
+        return;
     }
+    cancelEventsForAnrLocked(connection);
+}
+
+void InputDispatcher::doNotifyConnectionResponsiveLockedInterruptible(CommandEntry* commandEntry) {
+    mLock.unlock();
+
+    mPolicy->notifyConnectionResponsive(commandEntry->connectionToken);
+
+    mLock.lock();
 }
 
 void InputDispatcher::doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) {
@@ -5033,35 +5042,6 @@
     mLock.lock();
 }
 
-void InputDispatcher::extendAnrTimeoutsLocked(
-        const std::shared_ptr<InputApplicationHandle>& application,
-        const sp<IBinder>& connectionToken, std::chrono::nanoseconds timeoutExtension) {
-    if (connectionToken == nullptr && application != nullptr) {
-        // The ANR happened because there's no focused window
-        mNoFocusedWindowTimeoutTime = now() + timeoutExtension.count();
-        mAwaitedFocusedApplication = application;
-    }
-
-    sp<Connection> connection = getConnectionLocked(connectionToken);
-    if (connection == nullptr) {
-        // It's possible that the connection already disappeared. No action necessary.
-        return;
-    }
-
-    ALOGI("Raised ANR, but the policy wants to keep waiting on %s for %" PRId64 "ms longer",
-          connection->inputChannel->getName().c_str(), millis(timeoutExtension));
-
-    connection->responsive = true;
-    const nsecs_t newTimeout = now() + timeoutExtension.count();
-    for (DispatchEntry* entry : connection->waitQueue) {
-        if (newTimeout >= entry->timeoutTime) {
-            // Already removed old entries when connection was marked unresponsive
-            entry->timeoutTime = newTimeout;
-            mAnrTracker.insert(entry->timeoutTime, connectionToken);
-        }
-    }
-}
-
 void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
         CommandEntry* commandEntry) {
     KeyEntry& entry = *(commandEntry->keyEntry);
@@ -5149,10 +5129,19 @@
     if (dispatchEntryIt != connection->waitQueue.end()) {
         dispatchEntry = *dispatchEntryIt;
         connection->waitQueue.erase(dispatchEntryIt);
-        mAnrTracker.erase(dispatchEntry->timeoutTime,
-                          connection->inputChannel->getConnectionToken());
+        const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
+        mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
         if (!connection->responsive) {
             connection->responsive = isConnectionResponsive(*connection);
+            if (connection->responsive) {
+                // The connection was unresponsive, and now it's responsive. Tell the policy
+                // about it so that it can stop ANR.
+                std::unique_ptr<CommandEntry> connectionResponsiveCommand =
+                        std::make_unique<CommandEntry>(
+                                &InputDispatcher::doNotifyConnectionResponsiveLockedInterruptible);
+                connectionResponsiveCommand->connectionToken = connectionToken;
+                postCommandLocked(std::move(connectionResponsiveCommand));
+            }
         }
         traceWaitQueueLength(connection);
         if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 5387c40..5704a5a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -419,9 +419,6 @@
     // Once a connection becomes unresponsive, its entries are removed from AnrTracker to
     // prevent unneeded wakeups.
     AnrTracker mAnrTracker GUARDED_BY(mLock);
-    void extendAnrTimeoutsLocked(const std::shared_ptr<InputApplicationHandle>& application,
-                                 const sp<IBinder>& connectionToken,
-                                 std::chrono::nanoseconds timeoutExtension) REQUIRES(mLock);
 
     // Contains the last window which received a hover event.
     sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
@@ -475,9 +472,8 @@
                                        int32_t y) const REQUIRES(mLock);
     bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
     std::string dumpWindowForTouchOcclusion(const InputWindowInfo* info, bool isTouchWindow) const;
-    std::string getApplicationWindowLabel(
-            const std::shared_ptr<InputApplicationHandle>& applicationHandle,
-            const sp<InputWindowHandle>& windowHandle);
+    std::string getApplicationWindowLabel(const InputApplicationHandle* applicationHandle,
+                                          const sp<InputWindowHandle>& windowHandle);
 
     // Manage the dispatch cycle for a single connection.
     // These methods are deliberately not Interruptible because doing all of the work
@@ -554,11 +550,11 @@
     void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
             REQUIRES(mLock);
     void onAnrLocked(const Connection& connection) REQUIRES(mLock);
-    void onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) REQUIRES(mLock);
+    void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock);
     void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock);
     void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
             REQUIRES(mLock);
-    void updateLastAnrStateLocked(const std::shared_ptr<InputApplicationHandle>& application,
+    void updateLastAnrStateLocked(const InputApplicationHandle& application,
                                   const std::string& reason) REQUIRES(mLock);
     void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
             REQUIRES(mLock);
@@ -568,7 +564,11 @@
             REQUIRES(mLock);
     void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    void doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    void doNotifyConnectionUnresponsiveLockedInterruptible(CommandEntry* commandEntry)
+            REQUIRES(mLock);
+    void doNotifyConnectionResponsiveLockedInterruptible(CommandEntry* commandEntry)
+            REQUIRES(mLock);
     void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
             REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 463c5f1..1125257 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -45,11 +45,25 @@
     /* Notifies the system that a configuration change has occurred. */
     virtual void notifyConfigurationChanged(nsecs_t when) = 0;
 
-    /* Notifies the system that an application is not responding.
-     * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
-    virtual std::chrono::nanoseconds notifyAnr(
-            const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
-            const sp<IBinder>& token, const std::string& reason) = 0;
+    /* Notifies the system that an application does not have a focused window.
+     */
+    virtual void notifyNoFocusedWindowAnr(
+            const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) = 0;
+
+    /* Notifies the system that a connection just became unresponsive. This indicates that ANR
+     * should be raised for this connection. The connection is identified via token.
+     * The string reason contains information about the input event that we haven't received
+     * a response for.
+     */
+    virtual void notifyConnectionUnresponsive(const sp<IBinder>& token,
+                                              const std::string& reason) = 0;
+
+    /* Notifies the system that a connection just became responsive. This is only called after the
+     * connection was first marked "unresponsive". This indicates that ANR dialog (if any) should
+     * no longer should be shown to the user. The connection is eligible to cause a new ANR in the
+     * future.
+     */
+    virtual void notifyConnectionResponsive(const sp<IBinder>& token) = 0;
 
     /* Notifies the system that an input channel is unrecoverably broken. */
     virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 40471b2..5ab2ae3 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -126,35 +126,62 @@
 
     // This function must be called soon after the expected ANR timer starts,
     // because we are also checking how much time has passed.
-    void assertNotifyAnrWasCalled(
+    void assertNotifyNoFocusedWindowAnrWasCalled(
             std::chrono::nanoseconds timeout,
-            const std::shared_ptr<InputApplicationHandle>& expectedApplication,
-            const sp<IBinder>& expectedToken) {
-        std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData;
-        ASSERT_NO_FATAL_FAILURE(anrData = getNotifyAnrData(timeout));
-        ASSERT_EQ(expectedApplication, anrData.first);
-        ASSERT_EQ(expectedToken, anrData.second);
+            const std::shared_ptr<InputApplicationHandle>& expectedApplication) {
+        std::shared_ptr<InputApplicationHandle> application;
+        { // acquire lock
+            std::unique_lock lock(mLock);
+            android::base::ScopedLockAssertion assumeLocked(mLock);
+            ASSERT_NO_FATAL_FAILURE(
+                    application = getAnrTokenLockedInterruptible(timeout, mAnrApplications, lock));
+        } // release lock
+        ASSERT_EQ(expectedApplication, application);
     }
 
-    std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> getNotifyAnrData(
-            std::chrono::nanoseconds timeout) {
-        const std::chrono::time_point start = std::chrono::steady_clock::now();
+    void assertNotifyConnectionUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
+                                                     const sp<IBinder>& expectedConnectionToken) {
+        sp<IBinder> connectionToken = getUnresponsiveConnectionToken(timeout);
+        ASSERT_EQ(expectedConnectionToken, connectionToken);
+    }
+
+    void assertNotifyConnectionResponsiveWasCalled(const sp<IBinder>& expectedConnectionToken) {
+        sp<IBinder> connectionToken = getResponsiveConnectionToken();
+        ASSERT_EQ(expectedConnectionToken, connectionToken);
+    }
+
+    sp<IBinder> getUnresponsiveConnectionToken(std::chrono::nanoseconds timeout) {
         std::unique_lock lock(mLock);
-        std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
         android::base::ScopedLockAssertion assumeLocked(mLock);
+        return getAnrTokenLockedInterruptible(timeout, mAnrConnectionTokens, lock);
+    }
+
+    sp<IBinder> getResponsiveConnectionToken() {
+        std::unique_lock lock(mLock);
+        android::base::ScopedLockAssertion assumeLocked(mLock);
+        return getAnrTokenLockedInterruptible(0s, mResponsiveConnectionTokens, lock);
+    }
+
+    // All three ANR-related callbacks behave the same way, so we use this generic function to wait
+    // for a specific container to become non-empty. When the container is non-empty, return the
+    // first entry from the container and erase it.
+    template <class T>
+    T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage,
+                                     std::unique_lock<std::mutex>& lock) REQUIRES(mLock) {
+        const std::chrono::time_point start = std::chrono::steady_clock::now();
+        std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
 
         // If there is an ANR, Dispatcher won't be idle because there are still events
         // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
         // before checking if ANR was called.
-        // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide
-        // it some time to act. 100ms seems reasonable.
-        mNotifyAnr.wait_for(lock, timeToWait, [this]() REQUIRES(mLock) {
-            return !mAnrApplications.empty() && !mAnrWindowTokens.empty();
-        });
+        // Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need
+        // to provide it some time to act. 100ms seems reasonable.
+        mNotifyAnr.wait_for(lock, timeToWait,
+                            [&storage]() REQUIRES(mLock) { return !storage.empty(); });
         const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
-        if (mAnrApplications.empty() || mAnrWindowTokens.empty()) {
-            ADD_FAILURE() << "Did not receive ANR callback";
-            return {};
+        if (storage.empty()) {
+            ADD_FAILURE() << "Did not receive the ANR callback";
+            return nullptr;
         }
         // Ensure that the ANR didn't get raised too early. We can't be too strict here because
         // the dispatcher started counting before this function was called
@@ -165,17 +192,18 @@
                           << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
                           << "ms instead";
         }
-        std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> result =
-                std::make_pair(mAnrApplications.front(), mAnrWindowTokens.front());
-        mAnrApplications.pop();
-        mAnrWindowTokens.pop();
-        return result;
+        T token = storage.front();
+        storage.pop();
+        return token;
     }
 
     void assertNotifyAnrWasNotCalled() {
         std::scoped_lock lock(mLock);
         ASSERT_TRUE(mAnrApplications.empty());
-        ASSERT_TRUE(mAnrWindowTokens.empty());
+        ASSERT_TRUE(mAnrConnectionTokens.empty());
+        ASSERT_TRUE(mResponsiveConnectionTokens.empty())
+                << "ANR was not called, but please also consume the 'connection is responsive' "
+                   "signal";
     }
 
     void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
@@ -183,8 +211,6 @@
         mConfig.keyRepeatDelay = delay;
     }
 
-    void setAnrTimeout(std::chrono::nanoseconds timeout) { mAnrTimeout = timeout; }
-
 private:
     std::mutex mLock;
     std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -194,23 +220,33 @@
 
     // ANR handling
     std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
-    std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
+    std::queue<sp<IBinder>> mAnrConnectionTokens GUARDED_BY(mLock);
+    std::queue<sp<IBinder>> mResponsiveConnectionTokens GUARDED_BY(mLock);
     std::condition_variable mNotifyAnr;
-    std::chrono::nanoseconds mAnrTimeout = 0ms;
 
     void notifyConfigurationChanged(nsecs_t when) override {
         std::scoped_lock lock(mLock);
         mConfigurationChangedTime = when;
     }
 
-    std::chrono::nanoseconds notifyAnr(const std::shared_ptr<InputApplicationHandle>& application,
-                                       const sp<IBinder>& windowToken,
-                                       const std::string&) override {
+    void notifyConnectionUnresponsive(const sp<IBinder>& connectionToken,
+                                      const std::string&) override {
         std::scoped_lock lock(mLock);
-        mAnrApplications.push(application);
-        mAnrWindowTokens.push(windowToken);
+        mAnrConnectionTokens.push(connectionToken);
         mNotifyAnr.notify_all();
-        return mAnrTimeout;
+    }
+
+    void notifyConnectionResponsive(const sp<IBinder>& connectionToken) override {
+        std::scoped_lock lock(mLock);
+        mResponsiveConnectionTokens.push(connectionToken);
+        mNotifyAnr.notify_all();
+    }
+
+    void notifyNoFocusedWindowAnr(
+            const std::shared_ptr<InputApplicationHandle>& applicationHandle) override {
+        std::scoped_lock lock(mLock);
+        mAnrApplications.push(applicationHandle);
+        mNotifyAnr.notify_all();
     }
 
     void notifyInputChannelBroken(const sp<IBinder>&) override {}
@@ -811,6 +847,12 @@
                      expectedFlags);
     }
 
+    void consumeMotionOutside(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+                              int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE, expectedDisplayId,
+                     expectedFlags);
+    }
+
     void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) {
         ASSERT_NE(mInputReceiver, nullptr)
                 << "Cannot consume events from a window with no receiver";
@@ -1761,9 +1803,11 @@
     std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
     ASSERT_TRUE(consumeSeq);
 
-    mFakePolicy->assertNotifyAnrWasCalled(DISPATCHING_TIMEOUT, nullptr, monitor.getToken());
+    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(DISPATCHING_TIMEOUT,
+                                                             monitor.getToken());
     monitor.finishEvent(*consumeSeq);
     ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(monitor.getToken());
 }
 
 TEST_F(InputDispatcherTest, TestMoveEvent) {
@@ -2882,13 +2926,13 @@
     std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
     ASSERT_TRUE(sequenceNum);
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
 
-    // The remaining lines are not really needed for the test, but kept as a sanity check
     mWindow->finishEvent(*sequenceNum);
     mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
                           ADISPLAY_ID_DEFAULT, 0 /*flags*/);
     ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
 }
 
 // Send a key to the app and have the app not respond right away.
@@ -2898,7 +2942,7 @@
     std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
     ASSERT_TRUE(sequenceNum);
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
     ASSERT_TRUE(mDispatcher->waitForIdle());
 }
 
@@ -2924,19 +2968,16 @@
                       InputEventInjectionSync::WAIT_FOR_RESULT, 10ms);
     ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
     const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+    mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
     ASSERT_TRUE(mDispatcher->waitForIdle());
 }
 
 // We have a focused application, but no focused window
-// If the policy wants to keep waiting on the focused window to be added, make sure
-// that this timeout extension is honored and ANR is raised again.
-TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) {
+// Make sure that we don't notify policy twice about the same ANR.
+TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DoesNotSendDuplicateAnr) {
     mWindow->setFocusable(false);
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
     mWindow->consumeFocusEvent(false);
-    const std::chrono::duration timeout = 5ms;
-    mFakePolicy->setAnrTimeout(timeout);
 
     // Once a focused event arrives, we get an ANR for this application
     // We specify the injection timeout to be smaller than the application timeout, to ensure that
@@ -2947,14 +2988,14 @@
     ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
     const std::chrono::duration appTimeout =
             mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(appTimeout, mApplication, nullptr /*windowToken*/);
+    mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(appTimeout, mApplication);
 
-    // After the extended time has passed, ANR should be raised again
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+    std::this_thread::sleep_for(appTimeout);
+    // ANR should not be raised again. It is up to policy to do that if it desires.
+    mFakePolicy->assertNotifyAnrWasNotCalled();
 
-    // If we stop extending the timeout, dispatcher should go to idle.
-    // Another ANR may be raised during this time
-    mFakePolicy->setAnrTimeout(0ms);
+    // If we now get a focused window, the ANR should stop, but the policy handles that via
+    // 'notifyFocusChanged' callback. This is implemented in the policy so we can't test it here.
     ASSERT_TRUE(mDispatcher->waitForIdle());
 }
 
@@ -2971,7 +3012,7 @@
     ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
 
     const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+    mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
 
     // Future focused events get dropped right away
     ASSERT_EQ(InputEventInjectionResult::FAILED, injectKeyDown(mDispatcher));
@@ -3006,7 +3047,7 @@
     // We have now sent down and up. Let's consume first event and then ANR on the second.
     mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
 }
 
 // If an app is not responding to a key event, gesture monitors should continue to receive
@@ -3023,7 +3064,7 @@
 
     // Stuck on the ACTION_UP
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
 
     // New tap will go to the gesture monitor, but not to the window
     tapOnWindow();
@@ -3032,6 +3073,7 @@
 
     mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion
     mDispatcher->waitForIdle();
+    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
     mWindow->assertNoEvents();
     monitor.assertNoEvents();
 }
@@ -3050,7 +3092,7 @@
     mWindow->consumeMotionDown();
     // Stuck on the ACTION_UP
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
 
     // New tap will go to the gesture monitor, but not to the window
     tapOnWindow();
@@ -3059,6 +3101,7 @@
 
     mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion
     mDispatcher->waitForIdle();
+    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
     mWindow->assertNoEvents();
     monitor.assertNoEvents();
 }
@@ -3075,46 +3118,43 @@
     mWindow->consumeMotionDown();
     // Block on ACTION_UP
     const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
     mWindow->consumeMotionUp(); // Now the connection should be healthy again
     mDispatcher->waitForIdle();
+    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
     mWindow->assertNoEvents();
 
     tapOnWindow();
     mWindow->consumeMotionDown();
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken());
     mWindow->consumeMotionUp();
 
     mDispatcher->waitForIdle();
+    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
     mWindow->assertNoEvents();
 }
 
-// If the policy tells us to raise ANR again after some time, ensure that the timeout extension
-// is honored
-TEST_F(InputDispatcherSingleWindowAnr, Policy_CanExtendTimeout) {
-    const std::chrono::duration timeout = 5ms;
-    mFakePolicy->setAnrTimeout(timeout);
-
+// If a connection remains unresponsive for a while, make sure policy is only notified once about
+// it.
+TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) {
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                WINDOW_LOCATION));
 
     const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(windowTimeout, nullptr /*application*/,
-                                          mWindow->getToken());
-
-    // Since the policy wanted to extend ANR, make sure it is called again after the extension
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
-    mFakePolicy->setAnrTimeout(0ms);
+    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(windowTimeout, mWindow->getToken());
     std::this_thread::sleep_for(windowTimeout);
-    // We are not checking if ANR has been called, because it may have been called again by the
-    // time we set the timeout to 0
-
-    // When the policy finally says stop, we should get ACTION_CANCEL
+    // 'notifyConnectionUnresponsive' should only be called once per connection
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+    // When the ANR happened, dispatcher should abort the current event stream via ACTION_CANCEL
     mWindow->consumeMotionDown();
     mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
                           ADISPLAY_ID_DEFAULT, 0 /*flags*/);
     mWindow->assertNoEvents();
+    mDispatcher->waitForIdle();
+    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
 }
 
 /**
@@ -3278,17 +3318,22 @@
                                FOCUSED_WINDOW_LOCATION));
     std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
     ASSERT_TRUE(unfocusedSequenceNum);
-    std::optional<uint32_t> focusedSequenceNum = mFocusedWindow->receiveEvent();
-    ASSERT_TRUE(focusedSequenceNum);
 
     const std::chrono::duration timeout =
             mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
-                                          mFocusedWindow->getToken());
-
-    mFocusedWindow->finishEvent(*focusedSequenceNum);
+    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
+    // Because we injected two DOWN events in a row, CANCEL is enqueued for the first event
+    // sequence to make it consistent
+    mFocusedWindow->consumeMotionCancel();
     mUnfocusedWindow->finishEvent(*unfocusedSequenceNum);
+    mFocusedWindow->consumeMotionDown();
+    // This cancel is generated because the connection was unresponsive
+    mFocusedWindow->consumeMotionCancel();
+    mFocusedWindow->assertNoEvents();
+    mUnfocusedWindow->assertNoEvents();
     ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
 }
 
 // If we have 2 windows with identical timeouts that are both unresponsive,
@@ -3301,19 +3346,31 @@
 
     tapOnFocusedWindow();
     // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
-    std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData1 =
-            mFakePolicy->getNotifyAnrData(10ms);
-    std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData2 =
-            mFakePolicy->getNotifyAnrData(0ms);
+    sp<IBinder> anrConnectionToken1 = mFakePolicy->getUnresponsiveConnectionToken(10ms);
+    sp<IBinder> anrConnectionToken2 = mFakePolicy->getUnresponsiveConnectionToken(0ms);
 
     // We don't know which window will ANR first. But both of them should happen eventually.
-    ASSERT_TRUE(mFocusedWindow->getToken() == anrData1.second ||
-                mFocusedWindow->getToken() == anrData2.second);
-    ASSERT_TRUE(mUnfocusedWindow->getToken() == anrData1.second ||
-                mUnfocusedWindow->getToken() == anrData2.second);
+    ASSERT_TRUE(mFocusedWindow->getToken() == anrConnectionToken1 ||
+                mFocusedWindow->getToken() == anrConnectionToken2);
+    ASSERT_TRUE(mUnfocusedWindow->getToken() == anrConnectionToken1 ||
+                mUnfocusedWindow->getToken() == anrConnectionToken2);
 
     ASSERT_TRUE(mDispatcher->waitForIdle());
     mFakePolicy->assertNotifyAnrWasNotCalled();
+
+    mFocusedWindow->consumeMotionDown();
+    mFocusedWindow->consumeMotionUp();
+    mUnfocusedWindow->consumeMotionOutside();
+
+    sp<IBinder> responsiveToken1 = mFakePolicy->getResponsiveConnectionToken();
+    sp<IBinder> responsiveToken2 = mFakePolicy->getResponsiveConnectionToken();
+
+    // Both applications should be marked as responsive, in any order
+    ASSERT_TRUE(mFocusedWindow->getToken() == responsiveToken1 ||
+                mFocusedWindow->getToken() == responsiveToken2);
+    ASSERT_TRUE(mUnfocusedWindow->getToken() == responsiveToken1 ||
+                mUnfocusedWindow->getToken() == responsiveToken2);
+    mFakePolicy->assertNotifyAnrWasNotCalled();
 }
 
 // If a window is already not responding, the second tap on the same window should be ignored.
@@ -3330,8 +3387,7 @@
     ASSERT_TRUE(upEventSequenceNum);
     const std::chrono::duration timeout =
             mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
-                                          mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
 
     // Tap once again
     // We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
@@ -3351,7 +3407,8 @@
     ASSERT_TRUE(mDispatcher->waitForIdle());
     // The second tap did not go to the focused window
     mFocusedWindow->assertNoEvents();
-    // should not have another ANR after the window just became healthy again
+    // Since all events are finished, connection should be deemed healthy again
+    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken());
     mFakePolicy->assertNotifyAnrWasNotCalled();
 }
 
@@ -3437,6 +3494,7 @@
     mUnfocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
     mFocusedWindow->assertNoEvents();
     mUnfocusedWindow->assertNoEvents();
+    mFakePolicy->assertNotifyAnrWasNotCalled();
 }
 
 // When the touch stream is split across 2 windows, and one of them does not respond,
@@ -3462,8 +3520,7 @@
 
     const std::chrono::duration timeout =
             mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
-    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
-                                          mFocusedWindow->getToken());
+    mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken());
 
     mUnfocusedWindow->consumeMotionDown();
     mFocusedWindow->consumeMotionDown();
@@ -3481,10 +3538,12 @@
     } else {
         ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction());
     }
-
     ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken());
+
     mUnfocusedWindow->assertNoEvents();
     mFocusedWindow->assertNoEvents();
+    mFakePolicy->assertNotifyAnrWasNotCalled();
 }
 
 /**