目標窗口查找時,作為派發目標的窗口必須已經準備好接收新的輸入事件,否則判定窗口處于未響應狀態,終止事件的派發過程,并在一段時間后再試。倘若5s后窗口仍然未準備好接收輸入事件,將導致ANR。直接引發ANR的原因有很多,
例如Activity生命周期函數調用超時,
服務啟動超時
以及最常見的輸入事件處理超時等。
- Service ANR:前臺20s,后臺200s;startForeground超時10s
- Broadcast ANR:前臺10s,后臺60s
- Input ANR:按鍵或觸摸事件在5s內無響應
- ContentProvider ANR:10s,少見
下面從輸入事件超時的角度討論ANR的產生原因與過程
inputANR 分為兩種
無響應anr: 應用連接正常但是未相應事件,并且發生了超時
無焦點窗口anr: 當前有焦點應用,但是無焦點窗口,并且超時
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(&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);// 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);
}
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();// 無焦點anrmAwaitedFocusedApplication.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->inputChannel->getConnectionToken());onAnrLocked(connection);// 無響應anrreturn LLONG_MIN;
}
mNoFocusedWindowTimeoutTime 和 mAwaitedFocusedApplication 的設置是在 InputDispatcher::findFocusedWindowTargetLocked
函數中:
sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked(nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,InputEventInjectionResult& outInjectionResult) {std::string reason;outInjectionResult = InputEventInjectionResult::FAILED; // Default resultint32_t displayId = getTargetDisplayId(entry);sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);// If there is no currently focused window and no focused application// then drop the event.if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {ALOGI("Dropping %s event because there is no focused window or focused application in ""display %" PRId32 ".",ftl::enum_string(entry.type).c_str(), displayId);return nullptr;}// Drop key events if requested by input featureif (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {return nullptr;}// Compatibility behavior: raise ANR if there is a focused application, but no focused window.// Only start counting when we have a focused event to dispatch. The ANR is canceled if we// start interacting with another application via touch (app switch). This code can be removed// if the "no focused window ANR" is moved to the policy. Input doesn't know whether// an app is expected to have a focused window.if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {if (!mNoFocusedWindowTimeoutTime.has_value()) {// We just discovered that there's no focused window. Start the ANR timerstd::chrono::nanoseconds timeout = focusedApplicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);mNoFocusedWindowTimeoutTime = currentTime + timeout.count();mAwaitedFocusedApplication = focusedApplicationHandle;mAwaitedApplicationDisplayId = displayId;ALOGW("Waiting because no window has focus but %s may eventually add a ""window when it finishes starting up. Will wait for %" PRId64 "ms",mAwaitedFocusedApplication->getName().c_str(), millis(timeout));*nextWakeupTime = *mNoFocusedWindowTimeoutTime;outInjectionResult = InputEventInjectionResult::PENDING;return nullptr;} else if (currentTime > *mNoFocusedWindowTimeoutTime) {// Already raised ANR. Drop the eventALOGE("Dropping %s event because there is no focused window",ftl::enum_string(entry.type).c_str());return nullptr;} else {// Still waiting for the focused windowoutInjectionResult = InputEventInjectionResult::PENDING;return nullptr;}}// we have a valid, non-null focused windowresetNoFocusedWindowTimeoutLocked();// Verify targeted injection.if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) {ALOGW("Dropping injected event: %s", (*err).c_str());outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;return nullptr;}if (focusedWindowHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) {ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());outInjectionResult = InputEventInjectionResult::PENDING;return nullptr;}// If the event is a key event, then we must wait for all previous events to// complete before delivering it because previous events may have the// side-effect of transferring focus to a different window and we want to// ensure that the following keys are sent to the new window.//// Suppose the user touches a button in a window then immediately presses "A".// If the button causes a pop-up window to appear then we want to ensure that// the "A" key is delivered to the new pop-up window. This is because users// often anticipate pending UI changes when typing on a keyboard.// To obtain this behavior, we must serialize key events with respect to all// prior input events.if (entry.type == EventEntry::Type::KEY) {if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {*nextWakeupTime = *mKeyIsWaitingForEventsTimeout;outInjectionResult = InputEventInjectionResult::PENDING;return nullptr;}}outInjectionResult = InputEventInjectionResult::SUCCEEDED;return focusedWindowHandle;
}
可以看出引發 無焦點ANR 的兩個條件:
1.mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr 并且 currentTime >= *mNoFocusedWindowTimeoutTime
mNoFocusedWindowTimeoutTime 和 mAwaitedFocusedApplication 的設置是在 InputDispatcher::findFocusedWindowTargetsLocked 函數中:
當有焦點應用但沒有焦點窗口,設置?mNoFocusedWindowTimeoutTime 的值,開啟 ANR 倒計時。對于當前事件返回 PENDING 。
接著在當前的派發循環中執行 processAnrsLocked 函數判斷是否引發 ANR。
重置?mNoFocusedWindowTimeoutTime 和 mAwaitedFocusedApplication 是在 InputDispatcher::resetNoFocusedWindowTimeoutLocked 中。
執行?resetNoFocusedWindowTimeoutLocked 的幾個時機:
(1)InputDispatcher::findFocusedWindowTargetLocked中,當等待有焦點窗口后;
(2)InputDispatcher::setFocusedApplication 中,切換焦點應用后;
(3)InputDispatcher::setInputDispatchMode 中;
(4) InputDispatcher::resetAndDropEverythingLocked
無焦點anr
void InputDispatcher::processNoFocusedWindowAnrLocked() {// Check if the application that we are waiting for is still focused.std::shared_ptr<InputApplicationHandle> focusedApplication =getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);if (focusedApplication == nullptr ||focusedApplication->getApplicationToken() !=mAwaitedFocusedApplication->getApplicationToken()) {// Unexpected because we should have reset the ANR timer when focused application changedALOGE("Waited for a focused window, but focused application has already changed to %s",focusedApplication->getName().c_str());return; // The focused application has changed.}const sp<WindowInfoHandle>& focusedWindowHandle =getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);if (focusedWindowHandle != nullptr) {return; // We now have a focused window. No need for ANR.}onAnrLocked(mAwaitedFocusedApplication);
}
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, application = std::move(application)]() REQUIRES(mLock) {scoped_unlock unlock(mLock);mPolicy.notifyNoFocusedWindowAnr(application);};postCommandLocked(std::move(command));
}
?無響應anr
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->inputChannel->getName().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.begin();const nsecs_t currentWait = now() - oldestEntry->deliveryTime;std::string reason =android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",connection->inputChannel->getName().c_str(),ns2ms(currentWait),oldestEntry->eventEntry->getDescription().c_str());sp<IBinder> connectionToken = connection->inputChannel->getConnectionToken();updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);//處理無響應anr
processConnectionUnresponsiveLocked(*connection, std::move(reason));
// cnnel所有事件// Stop waking up for events on this connection, it is already unresponsivecancelEventsForAnrLocked(connection);
}
void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection,std::string reason) {const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();std::optional<int32_t> pid;if (connection.monitor) {ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(),reason.c_str());pid = findMonitorPidByTokenLocked(connectionToken);} else {// The connection is a windowALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(),reason.c_str());const sp<WindowInfoHandle> handle = getWindowHandleLocked(connectionToken);if (handle != nullptr) {pid = handle->getInfo()->ownerPid;}}
//發送窗口無響應命令sendWindowUnresponsiveCommandLocked(connectionToken, pid, std::move(reason));
}
//當這個command被執行時,調用的為doNotifyWindowUnresponsiveLockedInterruptible
void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {mLock.unlock();mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason); //調用到jni層,向java層傳遞mLock.lock();
}
ANR 的通知
// 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);
}
調用java層發送彈窗命令, 就不細說了