Merge "Add basic ANR test" into rvc-dev
diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h
index 7f04611..86de394 100644
--- a/include/input/InputApplication.h
+++ b/include/input/InputApplication.h
@@ -61,6 +61,11 @@
         return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
     }
 
+    inline std::chrono::nanoseconds getDispatchingTimeout(
+            std::chrono::nanoseconds defaultValue) const {
+        return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue;
+    }
+
     inline sp<IBinder> getApplicationToken() const {
         return mInfo.token;
     }
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index c5e56fd..2dac5b6 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -222,6 +222,11 @@
         return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
     }
 
+    inline std::chrono::nanoseconds getDispatchingTimeout(
+            std::chrono::nanoseconds defaultValue) const {
+        return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue;
+    }
+
     /**
      * Requests that the state of this object be updated to reflect
      * the most current available information about the application.
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index a1eb007..21c8ae1 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -102,6 +102,12 @@
     releaseInjectionState();
 }
 
+std::string EventEntry::getDescription() const {
+    std::string result;
+    appendDescription(result);
+    return result;
+}
+
 void EventEntry::release() {
     refCount -= 1;
     if (refCount == 0) {
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index ab481bd..a135409 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -83,6 +83,8 @@
 
     virtual void appendDescription(std::string& msg) const = 0;
 
+    std::string getDescription() const;
+
 protected:
     EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags);
     virtual ~EventEntry();
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index a21d1eb..e6e3347 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -78,7 +78,7 @@
 
 // Default input dispatching timeout if there is no focused application or paused window
 // from which to determine an appropriate dispatching timeout.
-constexpr nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+constexpr std::chrono::nanoseconds DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s;
 
 // Amount of time to allow for all pending events to be processed when an app switch
 // key is on the way.  This is used to preempt input dispatch and drop input events
@@ -1295,11 +1295,9 @@
         }
     } else {
         if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
-            if (DEBUG_FOCUS) {
-                ALOGD("Waiting for application to become ready for input: %s.  Reason: %s",
-                      getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
-            }
-            nsecs_t timeout;
+            ALOGI("Waiting for application to become ready for input: %s.  Reason: %s",
+                  getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
+            std::chrono::nanoseconds timeout;
             if (windowHandle != nullptr) {
                 timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
             } else if (applicationHandle != nullptr) {
@@ -1311,7 +1309,7 @@
 
             mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
             mInputTargetWaitStartTime = currentTime;
-            mInputTargetWaitTimeoutTime = currentTime + timeout;
+            mInputTargetWaitTimeoutTime = currentTime + timeout.count();
             mInputTargetWaitTimeoutExpired = false;
             mInputTargetWaitApplicationToken.clear();
 
@@ -1353,10 +1351,10 @@
 }
 
 void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(
-        nsecs_t newTimeout, const sp<IBinder>& inputConnectionToken) {
-    if (newTimeout > 0) {
+        nsecs_t timeoutExtension, const sp<IBinder>& inputConnectionToken) {
+    if (timeoutExtension > 0) {
         // Extend the timeout.
-        mInputTargetWaitTimeoutTime = now() + newTimeout;
+        mInputTargetWaitTimeoutTime = now() + timeoutExtension;
     } else {
         // Give up.
         mInputTargetWaitTimeoutExpired = true;
@@ -4048,11 +4046,12 @@
             const int32_t displayId = it.first;
             const sp<InputApplicationHandle>& applicationHandle = it.second;
             dump += StringPrintf(INDENT2 "displayId=%" PRId32
-                                         ", name='%s', dispatchingTimeout=%0.3fms\n",
+                                         ", name='%s', dispatchingTimeout=%" PRId64 "ms\n",
                                  displayId, applicationHandle->getName().c_str(),
-                                 applicationHandle->getDispatchingTimeout(
-                                         DEFAULT_INPUT_DISPATCHING_TIMEOUT) /
-                                         1000000.0);
+                                 ns2ms(applicationHandle
+                                               ->getDispatchingTimeout(
+                                                       DEFAULT_INPUT_DISPATCHING_TIMEOUT)
+                                               .count()));
         }
     } else {
         dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
@@ -4132,9 +4131,10 @@
                                          windowInfo->windowXScale, windowInfo->windowYScale);
                     dumpRegion(dump, windowInfo->touchableRegion);
                     dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
-                    dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+                    dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
+                                         "ms\n",
                                          windowInfo->ownerPid, windowInfo->ownerUid,
-                                         windowInfo->dispatchingTimeout / 1000000.0);
+                                         ns2ms(windowInfo->dispatchingTimeout));
                 }
             } else {
                 dump += INDENT2 "Windows: <none>\n";
@@ -4167,7 +4167,7 @@
         for (EventEntry* entry : mRecentQueue) {
             dump += INDENT2;
             entry->appendDescription(dump);
-            dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
+            dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
         }
     } else {
         dump += INDENT "RecentQueue: <empty>\n";
@@ -4178,8 +4178,8 @@
         dump += INDENT "PendingEvent:\n";
         dump += INDENT2;
         mPendingEvent->appendDescription(dump);
-        dump += StringPrintf(", age=%0.1fms\n",
-                             (currentTime - mPendingEvent->eventTime) * 0.000001f);
+        dump += StringPrintf(", age=%" PRId64 "ms\n",
+                             ns2ms(currentTime - mPendingEvent->eventTime));
     } else {
         dump += INDENT "PendingEvent: <none>\n";
     }
@@ -4190,7 +4190,7 @@
         for (EventEntry* entry : mInboundQueue) {
             dump += INDENT2;
             entry->appendDescription(dump);
-            dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
+            dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
         }
     } else {
         dump += INDENT "InboundQueue: <empty>\n";
@@ -4225,9 +4225,10 @@
                 for (DispatchEntry* entry : connection->outboundQueue) {
                     dump.append(INDENT4);
                     entry->eventEntry->appendDescription(dump);
-                    dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n",
+                    dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64
+                                         "ms\n",
                                          entry->targetFlags, entry->resolvedAction,
-                                         (currentTime - entry->eventEntry->eventTime) * 0.000001f);
+                                         ns2ms(currentTime - entry->eventEntry->eventTime));
                 }
             } else {
                 dump += INDENT3 "OutboundQueue: <empty>\n";
@@ -4240,10 +4241,10 @@
                     dump += INDENT4;
                     entry->eventEntry->appendDescription(dump);
                     dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, "
-                                         "age=%0.1fms, wait=%0.1fms\n",
+                                         "age=%" PRId64 "ms, wait=%" PRId64 "ms\n",
                                          entry->targetFlags, entry->resolvedAction,
-                                         (currentTime - entry->eventEntry->eventTime) * 0.000001f,
-                                         (currentTime - entry->deliveryTime) * 0.000001f);
+                                         ns2ms(currentTime - entry->eventEntry->eventTime),
+                                         ns2ms(currentTime - entry->deliveryTime));
                 }
             } else {
                 dump += INDENT3 "WaitQueue: <empty>\n";
@@ -4254,16 +4255,16 @@
     }
 
     if (isAppSwitchPendingLocked()) {
-        dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n",
-                             (mAppSwitchDueTime - now()) / 1000000.0);
+        dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n",
+                             ns2ms(mAppSwitchDueTime - now()));
     } else {
         dump += INDENT "AppSwitch: not pending\n";
     }
 
     dump += INDENT "Configuration:\n";
-    dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f);
-    dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n",
-                         mConfig.keyRepeatTimeout * 0.000001f);
+    dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay));
+    dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n",
+                         ns2ms(mConfig.keyRepeatTimeout));
 }
 
 void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) {
@@ -4365,8 +4366,7 @@
         return BAD_VALUE;
     }
 
-    [[maybe_unused]] const bool removed = removeByValue(mConnectionsByFd, connection);
-    ALOG_ASSERT(removed);
+    removeConnectionLocked(connection);
     mInputChannelsByToken.erase(inputChannel->getConnectionToken());
 
     if (connection->monitor) {
@@ -4468,7 +4468,7 @@
     return std::nullopt;
 }
 
-sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) {
+sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const {
     if (inputConnectionToken == nullptr) {
         return nullptr;
     }
@@ -4483,6 +4483,10 @@
     return nullptr;
 }
 
+void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
+    removeByValue(mConnectionsByFd, connection);
+}
+
 void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
                                                     const sp<Connection>& connection, uint32_t seq,
                                                     bool handled) {
@@ -4587,12 +4591,12 @@
             commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
     mLock.unlock();
 
-    nsecs_t newTimeout =
+    const nsecs_t timeoutExtension =
             mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
 
     mLock.lock();
 
-    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, token);
+    resumeAfterTargetsNotReadyTimeoutLocked(timeoutExtension, token);
 }
 
 void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
@@ -4647,11 +4651,8 @@
 
     const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
     if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
-        std::string msg =
-                StringPrintf("Window '%s' spent %0.1fms processing the last input event: ",
-                             connection->getWindowName().c_str(), eventDuration * 0.000001f);
-        dispatchEntry->eventEntry->appendDescription(msg);
-        ALOGI("%s", msg.c_str());
+        ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
+              ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
     }
     reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled);
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 89b5089..ff7be87 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -197,6 +197,11 @@
     // All registered connections mapped by channel file descriptor.
     std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
 
+    sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
+            REQUIRES(mLock);
+
+    void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
+
     struct IBinderHash {
         std::size_t operator()(const sp<IBinder>& b) const {
             return std::hash<IBinder*>{}(b.get());
@@ -209,7 +214,6 @@
     std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
             REQUIRES(mLock);
 
-    sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) REQUIRES(mLock);
 
     // Input channels that will receive a copy of all input events sent to the provided display.
     std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f33cc65..13e8354 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -17,12 +17,14 @@
 #include "../dispatcher/InputDispatcher.h"
 
 #include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 #include <binder/Binder.h>
 #include <input/Input.h>
 
 #include <gtest/gtest.h>
 #include <linux/input.h>
 #include <cinttypes>
+#include <thread>
 #include <unordered_set>
 #include <vector>
 
@@ -119,6 +121,33 @@
                 << "Expected onPointerDownOutsideFocus to not have been called";
     }
 
+    // This function must be called soon after the expected ANR timer starts,
+    // because we are also checking how much time has passed.
+    void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout,
+                                  const sp<InputApplicationHandle>& expectedApplication,
+                                  const sp<IBinder>& expectedToken) {
+        const std::chrono::time_point start = std::chrono::steady_clock::now();
+        std::unique_lock lock(mLock);
+        std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
+        android::base::ScopedLockAssertion assumeLocked(mLock);
+
+        // 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 mNotifyAnrWasCalled; });
+        const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
+        ASSERT_TRUE(mNotifyAnrWasCalled);
+        // 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
+        ASSERT_TRUE(timeout - 100ms < waited); // check (waited < timeout + 100ms) done by wait_for
+        mNotifyAnrWasCalled = false;
+        ASSERT_EQ(expectedApplication, mLastAnrApplication);
+        ASSERT_EQ(expectedToken, mLastAnrWindowToken);
+    }
+
     void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
         mConfig.keyRepeatTimeout = timeout;
         mConfig.keyRepeatDelay = delay;
@@ -131,14 +160,26 @@
     sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock);
     std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
 
+    // ANR handling
+    bool mNotifyAnrWasCalled GUARDED_BY(mLock) = false;
+    sp<InputApplicationHandle> mLastAnrApplication GUARDED_BY(mLock);
+    sp<IBinder> mLastAnrWindowToken GUARDED_BY(mLock);
+    std::condition_variable mNotifyAnr;
+    std::chrono::nanoseconds mAnrTimeout = 0ms;
+
     virtual void notifyConfigurationChanged(nsecs_t when) override {
         std::scoped_lock lock(mLock);
         mConfigurationChangedTime = when;
     }
 
-    virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>&, const sp<IBinder>&,
-                              const std::string&) override {
-        return 0;
+    virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& application,
+                              const sp<IBinder>& windowToken, const std::string&) override {
+        std::scoped_lock lock(mLock);
+        mLastAnrApplication = application;
+        mLastAnrWindowToken = windowToken;
+        mNotifyAnrWasCalled = true;
+        mNotifyAnr.notify_all();
+        return mAnrTimeout.count();
     }
 
     virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
@@ -309,6 +350,20 @@
         mFakePolicy.clear();
         mDispatcher.clear();
     }
+
+    /**
+     * Used for debugging when writing the test
+     */
+    void dumpDispatcherState() {
+        std::string dump;
+        mDispatcher->dump(dump);
+        std::stringstream ss(dump);
+        std::string to;
+
+        while (std::getline(ss, to, '\n')) {
+            ALOGE("%s", to.c_str());
+        }
+    }
 };
 
 
@@ -502,13 +557,20 @@
 
 class FakeApplicationHandle : public InputApplicationHandle {
 public:
-    FakeApplicationHandle() {}
+    FakeApplicationHandle() {
+        mInfo.name = "Fake Application";
+        mInfo.token = new BBinder();
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+    }
     virtual ~FakeApplicationHandle() {}
 
     virtual bool updateInfo() override {
-        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
         return true;
     }
+
+    void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
+        mInfo.dispatchingTimeout = timeout.count();
+    }
 };
 
 class FakeInputReceiver {
@@ -519,6 +581,20 @@
     }
 
     InputEvent* consume() {
+        InputEvent* event;
+        std::optional<uint32_t> consumeSeq = receiveEvent(&event);
+        if (!consumeSeq) {
+            return nullptr;
+        }
+        finishEvent(*consumeSeq);
+        return event;
+    }
+
+    /**
+     * Receive an event without acknowledging it.
+     * Return the sequence number that could later be used to send finished signal.
+     */
+    std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
         uint32_t consumeSeq;
         InputEvent* event;
 
@@ -535,23 +611,29 @@
 
         if (status == WOULD_BLOCK) {
             // Just means there's no event available.
-            return nullptr;
+            return std::nullopt;
         }
 
         if (status != OK) {
             ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK.";
-            return nullptr;
+            return std::nullopt;
         }
         if (event == nullptr) {
             ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer";
-            return nullptr;
+            return std::nullopt;
         }
+        if (outEvent != nullptr) {
+            *outEvent = event;
+        }
+        return consumeSeq;
+    }
 
-        status = mConsumer->sendFinishedSignal(consumeSeq, true);
-        if (status != OK) {
-            ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
-        }
-        return event;
+    /**
+     * To be used together with "receiveEvent" to complete the consumption of an event.
+     */
+    void finishEvent(uint32_t consumeSeq) {
+        const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true);
+        ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
     }
 
     void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
@@ -668,6 +750,10 @@
 
     void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; }
 
+    void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
+        mInfo.dispatchingTimeout = timeout.count();
+    }
+
     void setFrame(const Rect& frame) {
         mInfo.frameLeft = frame.left;
         mInfo.frameTop = frame.top;
@@ -740,6 +826,19 @@
                                      expectedFlags);
     }
 
+    std::optional<uint32_t> receiveEvent() {
+        if (mInputReceiver == nullptr) {
+            ADD_FAILURE() << "Invalid receive event on window with no receiver";
+            return std::nullopt;
+        }
+        return mInputReceiver->receiveEvent();
+    }
+
+    void finishEvent(uint32_t sequenceNum) {
+        ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
+        mInputReceiver->finishEvent(sequenceNum);
+    }
+
     InputEvent* consume() {
         if (mInputReceiver == nullptr) {
             return nullptr;
@@ -765,16 +864,15 @@
 
 std::atomic<int32_t> FakeWindowHandle::sId{1};
 
-static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
-        int32_t displayId = ADISPLAY_ID_NONE) {
+static int32_t injectKey(const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
+                         int32_t displayId = ADISPLAY_ID_NONE) {
     KeyEvent event;
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // Define a valid key down event.
     event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
-                     INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A,
-                     AMETA_NONE,
-                     /* repeatCount */ 0, currentTime, currentTime);
+                     INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
+                     repeatCount, currentTime, currentTime);
 
     // Inject event until dispatch out.
     return dispatcher->injectInputEvent(
@@ -783,10 +881,16 @@
             INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
 }
 
-static int32_t injectMotionEvent(const sp<InputDispatcher>& dispatcher, int32_t action,
-                                 int32_t source, int32_t displayId, int32_t x, int32_t y,
-                                 int32_t xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                                 int32_t yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION) {
+static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
+                             int32_t displayId = ADISPLAY_ID_NONE) {
+    return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
+}
+
+static int32_t injectMotionEvent(
+        const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
+        const PointF& position,
+        const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                        AMOTION_EVENT_INVALID_CURSOR_POSITION}) {
     MotionEvent event;
     PointerProperties pointerProperties[1];
     PointerCoords pointerCoords[1];
@@ -796,8 +900,8 @@
     pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
 
     pointerCoords[0].clear();
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x);
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y);
 
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid motion down event.
@@ -806,7 +910,7 @@
                      /* flags */ 0,
                      /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
                      /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
-                     /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition,
+                     /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y,
                      currentTime, currentTime,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
 
@@ -819,14 +923,12 @@
 
 static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
                                 int32_t displayId, const PointF& location = {100, 200}) {
-    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location.x,
-                             location.y);
+    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location);
 }
 
 static int32_t injectMotionUp(const sp<InputDispatcher>& dispatcher, int32_t source,
                               int32_t displayId, const PointF& location = {100, 200}) {
-    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location.x,
-                             location.y);
+    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
 }
 
 static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
@@ -1051,7 +1153,7 @@
     // left window. This event should be dispatched to the left window.
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
               injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
-                                ADISPLAY_ID_DEFAULT, 610, 400, 599, 400));
+                                ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400}));
     windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowRight->assertNoEvents();
 }
@@ -2185,4 +2287,82 @@
     consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
 }
 
+class InputDispatcherSingleWindowAnr : public InputDispatcherTest {
+    virtual void SetUp() override {
+        InputDispatcherTest::SetUp();
+
+        mApplication = new FakeApplicationHandle();
+        mApplication->setDispatchingTimeout(20ms);
+        mWindow =
+                new FakeWindowHandle(mApplication, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+        mWindow->setFrame(Rect(0, 0, 30, 30));
+        mWindow->setDispatchingTimeout(10ms);
+        mWindow->setFocus(true);
+        // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+        // window.
+        mWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+
+        // Set focused application.
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+        mWindow->consumeFocusEvent(true);
+    }
+
+    virtual void TearDown() override {
+        InputDispatcherTest::TearDown();
+        mWindow.clear();
+    }
+
+protected:
+    sp<FakeApplicationHandle> mApplication;
+    sp<FakeWindowHandle> mWindow;
+    static constexpr PointF WINDOW_LOCATION = {20, 20};
+
+    void tapOnWindow() {
+        ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+                  injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                   WINDOW_LOCATION));
+        ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+                  injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                 WINDOW_LOCATION));
+    }
+};
+
+// Send an event to the app and have the app not respond right away.
+// Make sure that ANR is raised
+TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               WINDOW_LOCATION));
+
+    // Also, overwhelm the socket to make sure ANR starts
+    for (size_t i = 0; i < 100; i++) {
+        injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                          ADISPLAY_ID_DEFAULT, {WINDOW_LOCATION.x, WINDOW_LOCATION.y + i});
+    }
+
+    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());
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// Send a key to the app and have the app not respond right away.
+TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) {
+    // Inject a key, and don't respond - expect that ANR is called.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+    std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
+    ASSERT_TRUE(sequenceNum);
+
+    // Start ANR process by sending a 2nd key, which would trigger the check for whether
+    // waitQueue is empty
+    injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 1);
+
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, mWindow->getToken());
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
 } // namespace android::inputdispatcher