frameworks 之 触摸事件ANR流程
- 1. 进入AnrTracker
- 2. 判断是否ANR
- 3. 正常响应
经常在应用开发中,会出现ANR 无响应的弹框,这是因为事件没在规定事件移除wq队列导致触发ANR。
涉及到的类如下
- frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
- frameworks/native/services/inputflinger/dispatcher/AnrTracker.cpp
- frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
- frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
- frameworks/base/services/java/com/android/server/SystemServer.java
- frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
- frameworks/base/services/core/java/com/android/server/wm/InputManagerCallback.java
- frameworks/base/services/core/java/com/android/server/wm/AnrController.java
- frameworks/base/core/java/android/app/ActivityManagerInternal.java
- frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- frameworks/base/services/core/java/com/android/server/am/AnrHelper.java
- frameworks/base/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
1. 进入AnrTracker
触摸事件分发文章 在进入移除oq,放数据进入wq 队列时候 ,最后对数据进行 timeoutTime 属性的标记。并将数据对应的时间和connection 放进去 mAnrTracker。
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection) {...// 记录派发时间,为后面anr计时dispatchEntry->deliveryTime = currentTime;const std::chrono::nanoseconds timeout =getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());// 超时时间dispatchEntry->timeoutTime = currentTime + timeout.count();...
// 将oq事件移除 添加到wqconnection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),connection->outboundQueue.end(),dispatchEntry));traceOutboundQueueLength(*connection);connection->waitQueue.push_back(dispatchEntry);// 插入数据进入 mAnrTracker 根据if (connection->responsive) {mAnrTracker.insert(dispatchEntry->timeoutTime,connection->inputChannel->getConnectionToken());}traceWaitQueueLength(*connection);
}
AnrTracker 的 insert 方法将参数 构造为 pair<nsecs_t /timeoutTime/, sp /connectionToken/> 放进去 multiset 数组
std::multiset<std::pair<nsecs_t /*timeoutTime*/, sp<IBinder> /*connectionToken*/>> mAnrTimeouts;void AnrTracker::insert(nsecs_t timeoutTime, sp<IBinder> token) {mAnrTimeouts.insert(std::make_pair(timeoutTime, std::move(token)));
}
2. 判断是否ANR
将对应的数据放进去 AnrTracker 里面后, 每次分发的线程都会唤醒,此时进入了 dispatchOnce 方法,该方法 processAnrsLocked 会解析是否触发 anr 是的话触发 anr。并返回下次唤醒的时间。
void InputDispatcher::dispatchOnce() {const nsecs_t nextAnrCheck = processAnrsLocked();nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
}
processAnrsLocked 方法会对里面的第一次的对象 超时时间做判断,取最小的时间,如果小于当前时间则触发 anr 。
// 检查是否有anr事件有则引发anr,没有返回下次唤醒的时间// 检查是否有anr事件有则引发anr,没有返回下次唤醒的时间
nsecs_t InputDispatcher::processAnrsLocked() {const nsecs_t currentTime = now();nsecs_t nextAnrCheck = LONG_LONG_MAX;// Check if we are waiting for a focused window to appear. Raise ANR if waited too long// 该判断用于 key 时间的判断if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {if (currentTime >= *mNoFocusedWindowTimeoutTime) {processNoFocusedWindowAnrLocked();mAwaitedFocusedApplication.reset();mNoFocusedWindowTimeoutTime = std::nullopt;return LONG_LONG_MIN;} else {// Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes.nextAnrCheck = *mNoFocusedWindowTimeoutTime;}}// Check if any connection ANRs are due// 检查首个超时时间和下一次 anr 检查时间,默认是 LONG_LONG_MAX 取最小那个// 如果大于当前时间,则返回下次唤醒时间// 否则则进入anr触发nextAnrCheck = 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.// 通过 token 获取对应的 connection,不为空 则sp<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 connection// 删除存在一样的 connection 数据mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());// 进入anr触发onAnrLocked(connection);return LONG_LONG_MIN;
}
进入 anr 后,判断对应的 wq 队列是否为空,为空则不触发。主要通过 updateLastAnrStateLocked 保存当前的一些状态。 processConnectionUnresponsiveLocked 进入触发 anr 留存。
// 触发anr
void InputDispatcher::onAnrLocked(const sp<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);processConnectionUnresponsiveLocked(*connection, std::move(reason));// Stop waking up for events on this connection, it is already unresponsivecancelEventsForAnrLocked(connection);
}
void InputDispatcher::updateLastAnrStateLocked(const sp<WindowInfoHandle>& window,const std::string& reason) {const std::string windowLabel = getApplicationWindowLabel(nullptr, window);updateLastAnrStateLocked(windowLabel, reason);
}void InputDispatcher::updateLastAnrStateLocked(const InputApplicationHandle& application,const std::string& reason) {const std::string windowLabel = getApplicationWindowLabel(&application, nullptr);updateLastAnrStateLocked(windowLabel, reason);
}void InputDispatcher::updateLastAnrStateLocked(const std::string& windowLabel,const std::string& reason) {// Capture a record of the InputDispatcher state at the time of the ANR.time_t t = time(nullptr);struct tm tm;localtime_r(&t, &tm);char timestr[64];strftime(timestr, sizeof(timestr), "%F %T", &tm);mLastAnrState.clear();mLastAnrState += INDENT "ANR:\n";mLastAnrState += StringPrintf(INDENT2 "Time: %s\n", timestr);mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason.c_str());mLastAnrState += StringPrintf(INDENT2 "Window: %s\n", windowLabel.c_str());dumpDispatchStateLocked(mLastAnrState);
}
processConnectionUnresponsiveLocked 会判断是否模拟器,正常的window后,则会执行命令 sendWindowUnresponsiveCommandLocked 命令。也是将 对应的函数放进命令数组中。
void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection,std::string reason) {const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();if (connection.monitor) {ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(),reason.c_str());std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken);if (!pid.has_value()) {ALOGE("Could not find unresponsive monitor for connection %s",connection.inputChannel->getName().c_str());return;}sendMonitorUnresponsiveCommandLocked(pid.value(), std::move(reason));return;}// If not a monitor, must be a windowALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(),reason.c_str());sendWindowUnresponsiveCommandLocked(connectionToken, std::move(reason));
}
放进去数组
void InputDispatcher::sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken,std::string reason) {std::unique_ptr<CommandEntry> windowUnresponsiveCommand = std::make_unique<CommandEntry>(&InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible);windowUnresponsiveCommand->connectionToken = std::move(connectionToken);windowUnresponsiveCommand->reason = std::move(reason);postCommandLocked(std::move(windowUnresponsiveCommand));
}
进入 方法后,之前文章也知道,最终调用 com_android_server_input_InputManagerService 的 notifyWindowUnresponsive 方法。
// 这里比较特殊,先解锁在执行对应的方法,在进行加锁
void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {mLock.unlock();mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason);mLock.lock();
}
最终又会通过 jni 进入 inputMangerService java 类的 notifyWindowUnresponsive。
void NativeInputManager::notifyWindowUnresponsive(const sp<IBinder>& token,const std::string& reason) {
#if DEBUG_INPUT_DISPATCHER_POLICYALOGD("notifyWindowUnresponsive");
#endifATRACE_CALL();JNIEnv* env = jniEnv();ScopedLocalFrame localFrame(env);jobject tokenObj = javaObjectForIBinder(env, token);ScopedLocalRef<jstring> reasonObj(env, env->NewStringUTF(reason.c_str()));env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyWindowUnresponsive, tokenObj,reasonObj.get());checkAndClearExceptionFromCallback(env, "notifyWindowUnresponsive");
}
进入 InputManagerService 的 notifyWindowUnresponsive 方法后又会调用 mWindowManagerCallbacks的方法,
mWindowManagerCallbacks 是从 InputManagerService 初始化后 通过 setWindowManagerCallbacks时候,传入的变量。(在SystemServer中)。可以看到 该callBack调用的是 wm对应的变量。
private void notifyWindowUnresponsive(IBinder token, String reason) {mWindowManagerCallbacks.notifyWindowUnresponsive(token, reason);}
// frameworks/base/services/java/com/android/server/SystemServer.java
t.traceBegin("StartInputManager");inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());// 通过start 启动事件监听循环inputManager.start();t.traceEnd();
而 WindowManagerService. 对应的 mInputManagerCallback 对象为 InputManagerCallback。所以查看 InputManagerCallback 对应实现的方法 又调用了 AnrController 下的方法
@Overridepublic void notifyWindowUnresponsive(@NonNull IBinder token, String reason) {mService.mAnrController.notifyWindowUnresponsive(token, reason);}
notifyWindowUnresponsive 方法主要 对 saveANRState 方法进行保存 ,最后在调用 inputDispatchingTimedOut 方法进行窗口提醒。mAmInternal 为 ActivityManagerInternal。其实现为 AMS的 内部类 LocalService。
void notifyWindowUnresponsive(IBinder inputToken, String reason) {preDumpIfLockTooSlow();final int pid;final boolean aboveSystem;final ActivityRecord activity;synchronized (mService.mGlobalLock) {InputTarget target = mService.getInputTargetFromToken(inputToken);if (target == null) {Slog.e(TAG_WM, "Unknown token, dropping notifyConnectionUnresponsive request");return;}WindowState windowState = target.getWindowState();pid = target.getPid();// Blame the activity if the input token belongs to the window. If the target is// embedded, then we will blame the pid instead.activity = (windowState.mInputChannelToken == inputToken)? windowState.mActivityRecord : null;Slog.i(TAG_WM, "ANR in " + target + ". Reason:" + reason);aboveSystem = isWindowAboveSystem(windowState);dumpAnrStateLocked(activity, windowState, reason);}if (activity != null) {activity.inputDispatchingTimedOut(reason, pid);} else {mService.mAmInternal.inputDispatchingTimedOut(pid, aboveSystem, reason);}}
查看对应的 inputDispatchingTimedOut 方法,又调用了 ActivityManagerService里面的inputDispatchingTimedOut 方法,该方法里面主要调用了 inputDispatchingTimedOut进一步处理。
@Overridepublic long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {return ActivityManagerService.this.inputDispatchingTimedOut(pid, aboveSystem, reason);}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {throw new SecurityException("Requires permission " + FILTER_EVENTS);}ProcessRecord proc;synchronized (mPidsSelfLocked) {proc = mPidsSelfLocked.get(pid);}final long timeoutMillis = proc != null ? proc.getInputDispatchingTimeoutMillis() :DEFAULT_DISPATCHING_TIMEOUT_MILLIS;if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) {return 0;}return timeoutMillis;}
inputDispatchingTimedOut 又调用了 AnrHelper 类的 appNotResponding 方法
boolean inputDispatchingTimedOut(ProcessRecord proc, String activityShortComponentName,ApplicationInfo aInfo, String parentShortComponentName,WindowProcessController parentProcess, boolean aboveSystem, String reason) {if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {throw new SecurityException("Requires permission " + FILTER_EVENTS);}final String annotation;if (reason == null) {annotation = "Input dispatching timed out";} else {annotation = "Input dispatching timed out (" + reason + ")";}if (proc != null) {synchronized (this) {if (proc.isDebugging()) {return false;}if (proc.getActiveInstrumentation() != null) {Bundle info = new Bundle();info.putString("shortMsg", "keyDispatchingTimedOut");info.putString("longMsg", annotation);finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);return true;}}mAnrHelper.appNotResponding(proc, activityShortComponentName, aInfo,parentShortComponentName, parentProcess, aboveSystem, annotation);}return true;}
appNotResponding 方法 构造对应的 AnrRecord 对象,并启动线程任务 startAnrConsumerIfNeeded
void appNotResponding(ProcessRecord anrProcess, String activityShortComponentName,ApplicationInfo aInfo, String parentShortComponentName,WindowProcessController parentProcess, boolean aboveSystem, String annotation) {synchronized (mAnrRecords) {mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,parentShortComponentName, parentProcess, aboveSystem, annotation));}startAnrConsumerIfNeeded();}
查看 AnrConsumerThread 的run 方法。通过 循环next函数 遍历 mAnrRecords 数组是否为空,不为空返回第一个,并计算时间 调用对应 ,调用对应的 AnrRecord 的 appNotResponding 方法。
private class AnrConsumerThread extends Thread {AnrConsumerThread() {super("AnrConsumer");}private AnrRecord next() {synchronized (mAnrRecords) {return mAnrRecords.isEmpty() ? null : mAnrRecords.remove(0);}}@Overridepublic void run() {AnrRecord r;while ((r = next()) != null) {scheduleBinderHeavyHitterAutoSamplerIfNecessary();final long startTime = SystemClock.uptimeMillis();// If there are many ANR at the same time, the latency may be larger. If the latency// is too large, the stack trace might not be meaningful.final long reportLatency = startTime - r.mTimestamp;final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS;r.appNotResponding(onlyDumpSelf);final long endTime = SystemClock.uptimeMillis();Slog.d(TAG, "Completed ANR of " + r.mApp.processName + " in "+ (endTime - startTime) + "ms, latency " + reportLatency+ (onlyDumpSelf ? "ms (expired, only dump ANR app)" : "ms"));}mRunning.set(false);synchronized (mAnrRecords) {// The race should be unlikely to happen. Just to make sure we don't miss.if (!mAnrRecords.isEmpty()) {startAnrConsumerIfNeeded();}}}}
接着又调用 ProcessErrorStateRecord 的 appNotResponding 方法。
void appNotResponding(boolean onlyDumpSelf) {mApp.mErrorState.appNotResponding(mActivityShortComponentName, mAppInfo,mParentShortComponentName, mParentProcess, mAboveSystem, mAnnotation,onlyDumpSelf);}
进入该方法 将各种 anr 的原因 拼接起来纪录下来,最终输出,并通过构造 AppNotRespondingDialog 方法 对应的message对象后,通过 hander 发送 SHOW_NOT_RESPONDING_UI_MSG 事件,进行弹框显示。整个anr 流程就结束了。
void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,String parentShortComponentName, WindowProcessController parentProcess,boolean aboveSystem, String annotation, boolean onlyDumpSelf) {ArrayList<Integer> firstPids = new ArrayList<>(5);SparseArray<Boolean> lastPids = new SparseArray<>(20);mApp.getWindowProcessController().appEarlyNotResponding(annotation, () -> {synchronized (mService) {mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR, true);}});...// Log the ANR to the main log.StringBuilder info = new StringBuilder();info.setLength(0);FrameworkStatsLog.write(FrameworkStatsLog.ANR_OCCURRED, mApp.uid, mApp.processName,activityShortComponentName == null ? "unknown" : activityShortComponentName,annotation,...final ProcessRecord parentPr = parentProcess != null? (ProcessRecord) parentProcess.mOwner : null;mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName,parentShortComponentName, parentPr, null, report.toString(), tracesFile,null, new Float(loadingProgress), incrementalMetrics, errorId);if (mApp.getWindowProcessController().appNotResponding(info.toString(),() -> {synchronized (mService) {mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR, true);}},() -> {synchronized (mService) {mService.mServices.scheduleServiceTimeoutLocked(mApp);}})) {return;}synchronized (mService) {...// mUiHandler can be null if the AMS is constructed with injector only. This will only// happen in tests.if (mService.mUiHandler != null) {// Bring up the infamous App Not Responding dialogMessage msg = Message.obtain();msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem);mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs);}}}
3. 正常响应
正常的触摸,需要移除对应的数据,移除的时机,在事件流程通过调用 doDispatchCycleFinishedLockedInterruptible 的时候,在移除wq的时候,会移除对应的 anr监听数据
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {...// 二次判断dispatchEntryIt = connection->findWaitQueueEntry(seq);if (dispatchEntryIt != connection->waitQueue.end()) {dispatchEntry = *dispatchEntryIt;// 删除wqconnection->waitQueue.erase(dispatchEntryIt);const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();// 移除对应的anr数据监听mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);...}// Start the next dispatch cycle for this connection.startDispatchCycleLocked(now(), connection);
}