ANR InputDispatching TimeOut超时判断 - android-15.0.0_r23
ANR InputDispatching TimeOut超时判断 - android-15.0.0_r23
- 1、时间限制常量
- 2、ANR输出信息
- 3 、超时逻辑
1、时间限制常量
DEFAULT_INPUT_DISPATCHING_TIMEOUT
一般与UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS
相等,是5s
HwTimeoutMultiplier()
: 调整硬件通信超时时间的乘数"ro.hw_timeout_multiplier"
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
const std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT = std::chrono::milliseconds(android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *HwTimeoutMultiplier());
frameworks/native/libs/input/android/os/IInputConstants.aidl
// This should be multiplied by the value of the system property ro.hw_timeout_multiplier before
// use. A pre-multiplied constant is available in Java in
// android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS.
const int UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds
system/libbase/include/android-base/properties.h
static inline int HwTimeoutMultiplier() {return android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
}
2、ANR输出信息
InputDispatcher::onAnrLocked
检测到anr
,通过JNI通知到InputManagerService
:
- notifyNoFocusedWindowAnr
- notifyWindowUnresponsive
通过InputManagerCallback
处理timeoutRecord
信息,最后AnrController
输出日志信息
frameworks/base/services/core/java/com/android/server/wm/InputManagerCallback.java
/*** Notifies the window manager about an application that is not responding because it has* no focused window.** Called by the InputManager.*/@Overridepublic void notifyNoFocusedWindowAnr(@NonNull InputApplicationHandle applicationHandle) {TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchNoFocusedWindow(timeoutMessage(OptionalInt.empty(), "Application does not have a focused window"));mService.mAnrController.notifyAppUnresponsive(applicationHandle, timeoutRecord);}@Overridepublic void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,String reason) {TimeoutRecord timeoutRecord = TimeoutRecord.forInputDispatchWindowUnresponsive(timeoutMessage(pid, reason));mService.mAnrController.notifyWindowUnresponsive(token, pid, timeoutRecord);}private String timeoutMessage(OptionalInt pid, String reason) {String message = (reason == null) ? "Input dispatching timed out.": String.format("Input dispatching timed out (%s).", reason);if (pid.isEmpty()) {return message;}StalledTransactionInfo stalledTransactionInfo =SurfaceControl.getStalledTransactionInfo(pid.getAsInt());if (stalledTransactionInfo == null) {return message;}return String.format("%s Buffer processing for the associated surface is stuck due to an "+ "unsignaled fence (window=%s, bufferId=0x%016X, frameNumber=%s). This "+ "potentially indicates a GPU hang.", message, stalledTransactionInfo.layerName,stalledTransactionInfo.bufferId, stalledTransactionInfo.frameNumber);}
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
// Native callback@SuppressWarnings("unused")private void notifyWindowUnresponsive(IBinder token, int pid, boolean isPidValid,String reason) {mWindowManagerCallbacks.notifyWindowUnresponsive(token,isPidValid ? OptionalInt.of(pid) : OptionalInt.empty(), reason);}// Native callback@SuppressWarnings("unused")private void notifyWindowResponsive(IBinder token, int pid, boolean isPidValid) {mWindowManagerCallbacks.notifyWindowResponsive(token,isPidValid ? OptionalInt.of(pid) : OptionalInt.empty());}
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
// ANR-related callbacks -- startvoid notifyNoFocusedWindowAnr(const std::shared_ptr<InputApplicationHandle>& handle) override;void notifyWindowUnresponsive(const sp<IBinder>& token, std::optional<gui::Pid> pid,const std::string& reason) override;void notifyWindowResponsive(const sp<IBinder>& token, std::optional<gui::Pid> pid) override;// ANR-related callbacks -- end
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::onAnrLocked(const std::shared_ptr<Connection>& connection) {if (connection == nullptr) {LOG_ALWAYS_FATAL("Caller must check for nullness");}// Since we are allowing the policy to extend the timeout, maybe the waitQueue// is already healthy again. Don't raise ANR in this situationif (connection->waitQueue.empty()) {ALOGI("Not raising ANR because the connection %s has recovered",connection->getInputChannelName().c_str());return;}/*** The "oldestEntry" is the entry that was first sent to the application. That entry, however,* may not be the one that caused the timeout to occur. One possibility is that window timeout* has changed. This could cause newer entries to time out before the already dispatched* entries. In that situation, the newest entries caused ANR. But in all likelihood, the app* processes the events linearly. So providing information about the oldest entry seems to be* most useful.*/DispatchEntry& oldestEntry = *connection->waitQueue.front();const nsecs_t currentWait = now() - oldestEntry.deliveryTime;std::string reason =android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",connection->getInputChannelName().c_str(),ns2ms(currentWait),oldestEntry.eventEntry->getDescription().c_str());sp<IBinder> connectionToken = connection->getToken();updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);processConnectionUnresponsiveLocked(*connection, std::move(reason));// Stop waking up for events on this connection, it is already unresponsivecancelEventsForAnrLocked(connection);
}void InputDispatcher::onAnrLocked(std::shared_ptr<InputApplicationHandle> application) {std::string reason =StringPrintf("%s does not have a focused window", application->getName().c_str());updateLastAnrStateLocked(*application, reason);auto command = [this, app = std::move(application)]() REQUIRES(mLock) {scoped_unlock unlock(mLock);mPolicy.notifyNoFocusedWindowAnr(app);};postCommandLocked(std::move(command));
}
3 、超时逻辑
正常情况,
ViewRootImpl
的InputStage
责任链处理完InputEvent
事件,都会finishInputEvent
通知InputDispatcher
执行doDispatchCycleFinishedCommand
进行发送下一个InputEvet
事件,此时会擦除Timeout
时间mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
若InputEvent
事件没有消费反馈处理,当dispatchOnce()
轮询时,检测processAnrsLocked()
,超时就会执行onAnrLocked
ANR输出信息。
frameworks/native/services/inputflinger/dispatcher/AnrTracker.h
frameworks/native/services/inputflinger/dispatcher/AnrTracker.cpp
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
frameworks/base/core/jni/android_view_InputEventSender.cpp
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LLONG_MAX;{ // acquire lockstd::scoped_lock _l(mLock);mDispatcherIsAlive.notify_all();// Run a dispatch loop if there are no pending commands.// The dispatch loop might enqueue commands to run afterwards.if (!haveCommandsLocked()) {dispatchOnceInnerLocked(/*byref*/ nextWakeupTime);}// Run all pending commands if there are any.// If any commands were run then force the next poll to wake up immediately.if (runCommandsLockedInterruptable()) {nextWakeupTime = LLONG_MIN;}// If we are still waiting for ack on some events,// we might have to wake up earlier to check if an app is anr'ing.const nsecs_t nextAnrCheck = processAnrsLocked();nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);if (mPerDeviceInputLatencyMetricsFlag) {processLatencyStatisticsLocked();}// We are about to enter an infinitely long sleep, because we have no commands or// pending or queued eventsif (nextWakeupTime == LLONG_MAX) {mDispatcherEnteredIdle.notify_all();}} // release lock// Wait for callback or timeout or wake. (make sure we round up, not down)nsecs_t currentTime = now();int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);mLooper->pollOnce(timeoutMillis);
}/*** Check if any of the connections' wait queues have events that are too old.* If we waited for events to be ack'ed for more than the window timeout, raise an ANR.* Return the time at which we should wake up next.*/
nsecs_t InputDispatcher::processAnrsLocked() {const nsecs_t currentTime = now();nsecs_t nextAnrCheck = LLONG_MAX;// Check if we are waiting for a focused window to appear. Raise ANR if waited too longif (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {if (currentTime >= *mNoFocusedWindowTimeoutTime) {processNoFocusedWindowAnrLocked();mAwaitedFocusedApplication.reset();mNoFocusedWindowTimeoutTime = std::nullopt;return LLONG_MIN;} else {// Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes.nextAnrCheck = *mNoFocusedWindowTimeoutTime;}}// Check if any connection ANRs are duenextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());if (currentTime < nextAnrCheck) { // most likely scenarioreturn nextAnrCheck; // everything is normal. Let's check again at nextAnrCheck}// If we reached here, we have an unresponsive connection.std::shared_ptr<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());if (connection == nullptr) {ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());return nextAnrCheck;}connection->responsive = false;// Stop waking up for this unresponsive connectionmAnrTracker.eraseToken(connection->getToken());onAnrLocked(connection);return LLONG_MIN;
}void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,const std::shared_ptr<Connection>& connection,uint32_t seq, bool handled, nsecs_t consumeTime) {if (DEBUG_DISPATCH_CYCLE) {ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",connection->getInputChannelName().c_str(), seq, toString(handled));}if (connection->status != Connection::Status::NORMAL) {return;}// Notify other system components and prepare to start the next dispatch cycle.auto command = [this, currentTime, connection, seq, handled, consumeTime]() REQUIRES(mLock) {doDispatchCycleFinishedCommand(currentTime, connection, seq, handled, consumeTime);};postCommandLocked(std::move(command));
}int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {std::scoped_lock _l(mLock);std::shared_ptr<Connection> connection = getConnectionLocked(connectionToken);if (connection == nullptr) {ALOGW("Received looper callback for unknown input channel token %p. events=0x%x",connectionToken.get(), events);return 0; // remove the callback}bool notify;if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {if (!(events & ALOOPER_EVENT_INPUT)) {ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. ""events=0x%x",connection->getInputChannelName().c_str(), events);return 1;}nsecs_t currentTime = now();bool gotOne = false;status_t status = OK;for (;;) {Result<InputPublisher::ConsumerResponse> result =connection->inputPublisher.receiveConsumerResponse();if (!result.ok()) {status = result.error().code();break;}if (std::holds_alternative<InputPublisher::Finished>(*result)) {const InputPublisher::Finished& finish =std::get<InputPublisher::Finished>(*result);finishDispatchCycleLocked(currentTime, connection, finish.seq, finish.handled,finish.consumeTime);} else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {if (shouldReportMetricsForConnection(*connection)) {const InputPublisher::Timeline& timeline =std::get<InputPublisher::Timeline>(*result);mLatencyTracker.trackGraphicsLatency(timeline.inputEventId,connection->getToken(),std::move(timeline.graphicsTimeline));}}gotOne = true;}if (gotOne) {runCommandsLockedInterruptable();if (status == WOULD_BLOCK) {return 1;}}notify = status != DEAD_OBJECT || !connection->monitor;if (notify) {ALOGE("channel '%s' ~ Failed to receive finished signal. status=%s(%d)",connection->getInputChannelName().c_str(), statusToString(status).c_str(),status);}} else {// Monitor channels are never explicitly unregistered.// We do it automatically when the remote endpoint is closed so don't warn about them.const bool stillHaveWindowHandle = getWindowHandleLocked(connection->getToken()) != nullptr;notify = !connection->monitor && stillHaveWindowHandle;if (notify) {ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x",connection->getInputChannelName().c_str(), events);}}// Remove the channel.removeInputChannelLocked(connection->getToken(), notify);return 0; // remove the callback
}void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,const std::shared_ptr<Connection>& connection,uint32_t seq, bool handled,nsecs_t consumeTime) {// Handle post-event policy actions.std::unique_ptr<const KeyEntry> fallbackKeyEntry;{ // Start critical sectionauto dispatchEntryIt =std::find_if(connection->waitQueue.begin(), connection->waitQueue.end(),[seq](auto& e) { return e->seq == seq; });if (dispatchEntryIt == connection->waitQueue.end()) {return;}DispatchEntry& dispatchEntry = **dispatchEntryIt;const nsecs_t eventDuration = finishTime - dispatchEntry.deliveryTime;if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {ALOGI("%s spent %" PRId64 "ms processing %s", connection->getInputChannelName().c_str(),ns2ms(eventDuration), dispatchEntry.eventEntry->getDescription().c_str());}if (shouldReportFinishedEvent(dispatchEntry, *connection)) {mLatencyTracker.trackFinishedEvent(dispatchEntry.eventEntry->id, connection->getToken(),dispatchEntry.deliveryTime, consumeTime, finishTime);}if (dispatchEntry.eventEntry->type == EventEntry::Type::KEY) {fallbackKeyEntry =afterKeyEventLockedInterruptable(connection, &dispatchEntry, handled);}} // End critical section: The -LockedInterruptable methods may have released the lock.// Dequeue the event and start the next cycle.// Because the lock might have been released, it is possible that the// contents of the wait queue to have been drained, so we need to double-check// a few things.auto entryIt = std::find_if(connection->waitQueue.begin(), connection->waitQueue.end(),[seq](auto& e) { return e->seq == seq; });if (entryIt != connection->waitQueue.end()) {std::unique_ptr<DispatchEntry> dispatchEntry = std::move(*entryIt);connection->waitQueue.erase(entryIt);const sp<IBinder>& connectionToken = connection->getToken();mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);if (!connection->responsive) {connection->responsive = isConnectionResponsive(*connection);if (connection->responsive) {// The connection was unresponsive, and now it's responsive.processConnectionResponsiveLocked(*connection);}}traceWaitQueueLength(*connection);if (fallbackKeyEntry && connection->status == Connection::Status::NORMAL) {const auto windowHandle = getWindowHandleLocked(connection->getToken());// Only dispatch fallbacks if there is a window for the connection.if (windowHandle != nullptr) {const auto inputTarget =createInputTargetLocked(windowHandle, InputTarget::DispatchMode::AS_IS,dispatchEntry->targetFlags,fallbackKeyEntry->downTime);if (inputTarget.has_value()) {enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry),*inputTarget);}}}releaseDispatchEntry(std::move(dispatchEntry));}// Start the next dispatch cycle for this connection.startDispatchCycleLocked(now(), connection);
}