From ffaa2b163055c644dec7c2c681ed8b608e48f6b9 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 26 May 2020 21:43:02 -0700 Subject: [PATCH] Add basic ANR test To reduce the delta of the upcoming ANR refactor, add a basic ANR test here. This will also help highlight the difference in behaviour from the current code to the new code. To cause an ANR today, the socket needs to be blocked, which means that we need to send ~ 50 events to the unresponsive window. These workarounds will be removed when the ANRs are refactored. Bug: 143459140 Test: adb shell -t /data/nativetest64/inputflinger_tests/inputflinger_tests --gtest_filter="*InputDispatcherSingleWindowAnr*" --gtest_repeat=1000 --gtest_break_on_failure Change-Id: I0a1b28c2785d03d8870691641e0f7c6b1ca3b85e Merged-In: I0a1b28c2785d03d8870691641e0f7c6b1ca3b85e --- include/input/InputApplication.h | 5 + include/input/InputWindow.h | 5 + services/inputflinger/dispatcher/Entry.cpp | 6 + services/inputflinger/dispatcher/Entry.h | 2 + .../dispatcher/InputDispatcher.cpp | 81 +++--- .../inputflinger/dispatcher/InputDispatcher.h | 6 +- .../tests/InputDispatcher_test.cpp | 240 +++++++++++++++--- 7 files changed, 274 insertions(+), 71 deletions(-) diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h index 7f04611309..86de394a31 100644 --- a/include/input/InputApplication.h +++ b/include/input/InputApplication.h @@ -61,6 +61,11 @@ public: 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 getApplicationToken() const { return mInfo.token; } diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index c5e56fd91f..2dac5b62a7 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -222,6 +222,11 @@ public: 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 a1eb0079df..21c8ae165d 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -102,6 +102,12 @@ EventEntry::~EventEntry() { 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 ab481bd411..a135409365 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -83,6 +83,8 @@ struct EventEntry { 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 a21d1ebd2d..e6e3347ae9 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -78,7 +78,7 @@ namespace android::inputdispatcher { // 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 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( } } 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 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( 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::removeWindowByTokenLocked(const sp& token) { } void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( - nsecs_t newTimeout, const sp& inputConnectionToken) { - if (newTimeout > 0) { + nsecs_t timeoutExtension, const sp& inputConnectionToken) { + if (timeoutExtension > 0) { // Extend the timeout. - mInputTargetWaitTimeoutTime = now() + newTimeout; + mInputTargetWaitTimeoutTime = now() + timeoutExtension; } else { // Give up. mInputTargetWaitTimeoutExpired = true; @@ -4048,11 +4046,12 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { const int32_t displayId = it.first; const sp& 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: \n"); @@ -4132,9 +4131,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { 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: \n"; @@ -4167,7 +4167,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { 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: \n"; @@ -4178,8 +4178,8 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { 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: \n"; } @@ -4190,7 +4190,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { 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: \n"; @@ -4225,9 +4225,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { 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: \n"; @@ -4240,10 +4241,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { 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: \n"; @@ -4254,16 +4255,16 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { } 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& monitors) { @@ -4365,8 +4366,7 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp& i 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 @@ std::optional InputDispatcher::findGestureMonitorDisplayByTokenLocked( return std::nullopt; } -sp InputDispatcher::getConnectionLocked(const sp& inputConnectionToken) { +sp InputDispatcher::getConnectionLocked(const sp& inputConnectionToken) const { if (inputConnectionToken == nullptr) { return nullptr; } @@ -4483,6 +4483,10 @@ sp InputDispatcher::getConnectionLocked(const sp& inputConn return nullptr; } +void InputDispatcher::removeConnectionLocked(const sp& connection) { + removeByValue(mConnectionsByFd, connection); +} + void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp& connection, uint32_t seq, bool handled) { @@ -4587,12 +4591,12 @@ void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) 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 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c 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 89b5089e49..ff7be87609 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -197,6 +197,11 @@ private: // All registered connections mapped by channel file descriptor. std::unordered_map> mConnectionsByFd GUARDED_BY(mLock); + sp getConnectionLocked(const sp& inputConnectionToken) const + REQUIRES(mLock); + + void removeConnectionLocked(const sp& connection) REQUIRES(mLock); + struct IBinderHash { std::size_t operator()(const sp& b) const { return std::hash{}(b.get()); @@ -209,7 +214,6 @@ private: std::optional findGestureMonitorDisplayByTokenLocked(const sp& token) REQUIRES(mLock); - sp getConnectionLocked(const sp& inputConnectionToken) REQUIRES(mLock); // Input channels that will receive a copy of all input events sent to the provided display. std::unordered_map> mGlobalMonitorsByDisplay GUARDED_BY(mLock); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index f33cc65c2a..13e835427f 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -17,12 +17,14 @@ #include "../dispatcher/InputDispatcher.h" #include +#include #include #include #include #include #include +#include #include #include @@ -119,6 +121,33 @@ public: << "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& expectedApplication, + const sp& 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 @@ private: sp mOnPointerDownToken GUARDED_BY(mLock); std::optional mLastNotifySwitch GUARDED_BY(mLock); + // ANR handling + bool mNotifyAnrWasCalled GUARDED_BY(mLock) = false; + sp mLastAnrApplication GUARDED_BY(mLock); + sp 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&, const sp&, - const std::string&) override { - return 0; + virtual nsecs_t notifyAnr(const sp& application, + const sp& 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&) override {} @@ -309,6 +350,20 @@ protected: 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 @@ static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s; 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 @@ public: } InputEvent* consume() { + InputEvent* event; + std::optional 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 receiveEvent(InputEvent** outEvent = nullptr) { uint32_t consumeSeq; InputEvent* event; @@ -535,23 +611,29 @@ public: 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 @@ public: 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 @@ public: expectedFlags); } + std::optional 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 @@ private: std::atomic FakeWindowHandle::sId{1}; -static int32_t injectKeyDown(const sp& dispatcher, - int32_t displayId = ADISPLAY_ID_NONE) { +static int32_t injectKey(const sp& 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 @@ static int32_t injectKeyDown(const sp& dispatcher, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } -static int32_t injectMotionEvent(const sp& 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& dispatcher, + int32_t displayId = ADISPLAY_ID_NONE) { + return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId); +} + +static int32_t injectMotionEvent( + const sp& 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 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t 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 @@ static int32_t injectMotionEvent(const sp& dispatcher, int32_t /* 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 injectMotionEvent(const sp& dispatcher, int32_t static int32_t injectMotionDown(const sp& 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& 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 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { // 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 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithSc 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 mApplication; + sp 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 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 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