Merge "Tell policy about responsive connections"
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 e5d208a..ff5f6d8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2336,7 +2336,7 @@
 }
 
 std::string InputDispatcher::getApplicationWindowLabel(
-        const std::shared_ptr<InputApplicationHandle>& applicationHandle,
+        const InputApplicationHandle* applicationHandle,
         const sp<InputWindowHandle>& windowHandle) {
     if (applicationHandle != nullptr) {
         if (windowHandle != nullptr) {
@@ -4922,24 +4922,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));
 }
 
@@ -4956,9 +4953,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);
 }
 
@@ -5006,24 +5003,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) {
@@ -5034,35 +5043,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);
@@ -5150,10 +5130,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();
 }
 
 /**