diff options
| -rw-r--r-- | include/input/InputApplication.h | 5 | ||||
| -rw-r--r-- | include/input/InputWindow.h | 5 | ||||
| -rw-r--r-- | libs/nativewindow/include/android/native_window.h | 8 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/Entry.cpp | 6 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/Entry.h | 2 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 81 | ||||
| -rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.h | 6 | ||||
| -rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 240 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/LayerHistoryV2.cpp | 4 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/LayerInfoV2.cpp | 11 | ||||
| -rw-r--r-- | services/surfaceflinger/Scheduler/LayerInfoV2.h | 10 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 2 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp | 74 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/mock/MockLayer.h | 5 |
14 files changed, 370 insertions, 89 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<IBinder> 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/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index 25130e2a03..36aad2eced 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -236,7 +236,11 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) __INTRODUCED_IN /** Compatibility value for ANativeWindow_setFrameRate. */ enum ANativeWindow_FrameRateCompatibility { /** - * There are no inherent restrictions on the frame rate of this window. + * There are no inherent restrictions on the frame rate of this window. When + * the system selects a frame rate other than what the app requested, the + * app will be able to run at the system frame rate without requiring pull + * down. This value should be used when displaying game content, UIs, and + * anything that isn't video. */ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0, /** @@ -246,7 +250,7 @@ enum ANativeWindow_FrameRateCompatibility { * to do pull down or use some other technique to adapt to the system's * frame rate. The user experience is likely to be worse (e.g. more frame * stuttering) than it would be if the system had chosen the app's requested - * frame rate. + * frame rate. This value should be used for video content. */ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1 }; 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<IBinder>& token) { } 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 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { 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 @@ 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: <none>\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: <empty>\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: <none>\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: <empty>\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: <empty>\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: <empty>\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<Monitor>& monitors) { @@ -4365,8 +4366,7 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& 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<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked( 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 @@ sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConn 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 @@ 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<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 @@ private: 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 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 <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 @@ 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<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 @@ private: 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 @@ 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<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 @@ 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; } - - status = mConsumer->sendFinishedSignal(consumeSeq, true); - if (status != OK) { - ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK."; + if (outEvent != nullptr) { + *outEvent = event; } - return event; + return consumeSeq; + } + + /** + * 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<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 @@ private: 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 @@ static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher, 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 @@ static int32_t injectMotionEvent(const sp<InputDispatcher>& 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<InputDispatcher>& 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<InputDispatcher>& dispatcher, int32_t 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 @@ 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<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 diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp index 67edef4344..6570b1aa4a 100644 --- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp @@ -192,7 +192,7 @@ void LayerHistoryV2::partitionLayers(nsecs_t now) { trace(weak, LayerHistory::LayerVoteType::NoVote, 0); } - info->clearHistory(); + info->onLayerInactive(now); std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]); } @@ -213,7 +213,7 @@ void LayerHistoryV2::clear() { std::lock_guard lock(mLock); for (const auto& [layer, info] : activeLayers()) { - info->clearHistory(); + info->clearHistory(systemTime()); } } } // namespace android::scheduler::impl diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp index 276afd8f09..25dca39038 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp @@ -57,21 +57,14 @@ bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const { } bool LayerInfoV2::isFrequent(nsecs_t now) const { - // Find the first valid frame time - auto it = mFrameTimes.begin(); - for (; it != mFrameTimes.end(); ++it) { - if (isFrameTimeValid(*it)) { - break; - } - } - // If we know nothing about this layer we consider it as frequent as it might be the start // of an animation. - if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) { + if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) { return true; } // Find the first active frame + auto it = mFrameTimes.begin(); for (; it != mFrameTimes.end(); ++it) { if (it->queueTime >= getActiveLayerThreshold(now)) { break; diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h index f2d29c5ea9..5f50d5a772 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.h +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h @@ -83,15 +83,21 @@ public: // updated time, the updated time is the present time. nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; } - void clearHistory() { + void onLayerInactive(nsecs_t now) { // Mark mFrameTimeValidSince to now to ignore all previous frame times. // We are not deleting the old frame to keep track of whether we should treat the first // buffer as Max as we don't know anything about this layer or Min as this layer is // posting infrequent updates. - mFrameTimeValidSince = std::chrono::steady_clock::now(); + const auto timePoint = std::chrono::nanoseconds(now); + mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint); mLastReportedRefreshRate = 0.0f; } + void clearHistory(nsecs_t now) { + onLayerInactive(now); + mFrameTimes.clear(); + } + private: // Used to store the layer timestamps struct FrameTimeData { diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 2d25319e2a..c3214f087b 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -3685,7 +3685,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (layer->setCornerRadius(s.cornerRadius)) flags |= eTraversalNeeded; } - if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs) { + if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs && mSupportsBlur) { if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded; } if (what & layer_state_t::eLayerStackChanged) { diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp index 431cf0f4b3..dc705ede8c 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp @@ -37,6 +37,7 @@ protected: static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE; static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS; static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE; + static constexpr auto PRESENT_TIME_HISTORY_TIME = LayerInfoV2::HISTORY_TIME; static constexpr float LO_FPS = 30.f; static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS); @@ -71,6 +72,9 @@ protected: } auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); } + auto createLayer(std::string name) { + return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name))); + } Hwc2::mock::Display mDisplay; RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0) @@ -541,5 +545,75 @@ TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) { EXPECT_EQ(2, frequentLayerCount(time)); } +class LayerHistoryTestV2Parameterized + : public LayerHistoryTestV2, + public testing::WithParamInterface<std::chrono::nanoseconds> {}; + +TEST_P(LayerHistoryTestV2Parameterized, HeuristicLayerWithInfrequentLayer) { + std::chrono::nanoseconds infrequentUpdateDelta = GetParam(); + auto heuristicLayer = createLayer("HeuristicLayer"); + + EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree()) + .WillRepeatedly(Return(Layer::FrameRate())); + + auto infrequentLayer = createLayer("InfrequentLayer"); + EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree()) + .WillRepeatedly(Return(Layer::FrameRate())); + + const nsecs_t startTime = systemTime(); + + const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns; + history().record(heuristicLayer.get(), startTime, startTime); + history().record(infrequentLayer.get(), startTime, startTime); + + nsecs_t time = startTime; + nsecs_t lastInfrequentUpdate = startTime; + const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5; + int infrequentLayerUpdates = 0; + while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) { + time += heuristicUpdateDelta.count(); + history().record(heuristicLayer.get(), time, time); + + if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) { + ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates, + totalInfrequentLayerUpdates); + lastInfrequentUpdate = time; + history().record(infrequentLayer.get(), time, time); + infrequentLayerUpdates++; + } + + if (time - startTime > PRESENT_TIME_HISTORY_TIME.count()) { + ASSERT_NE(0, history().summarize(time).size()); + ASSERT_GE(2, history().summarize(time).size()); + + bool max = false; + bool min = false; + float heuristic = 0; + for (const auto& layer : history().summarize(time)) { + if (layer.vote == LayerHistory::LayerVoteType::Heuristic) { + heuristic = layer.desiredRefreshRate; + } else if (layer.vote == LayerHistory::LayerVoteType::Max) { + max = true; + } else if (layer.vote == LayerHistory::LayerVoteType::Min) { + min = true; + } + } + + if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) { + EXPECT_FLOAT_EQ(24.0f, heuristic); + EXPECT_FALSE(max); + if (history().summarize(time).size() == 2) { + EXPECT_TRUE(min); + } + } + } + } +} + +INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestV2Parameterized, + ::testing::Values(1s, 2s, 3s, 4s, 5s)); + } // namespace } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h index 119f58033c..078d8e07f7 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h +++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h @@ -24,8 +24,9 @@ namespace android::mock { class MockLayer : public Layer { public: - explicit MockLayer(SurfaceFlinger* flinger) - : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 800, 600, 0, {})) {} + MockLayer(SurfaceFlinger* flinger, std::string name) + : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 800, 600, 0, {})) {} + explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {} MOCK_CONST_METHOD0(getType, const char*()); MOCK_METHOD0(getFrameSelectionPriority, int32_t()); |