平台
R K 3568 + A n d r o i d 11 RK3568 + Android 11RK3568+Android11
概述
官方的说明
如果 Android 应用的界面线程处于阻塞状态的时间过长,会触发“应用无响应”(ANR) 错误。如果应用位于前台,系统会向用户显示一个对话框,ANR 对话框会为用户提供强制退出应用的选项。
ANR 处理流程
首先, 3个ANR的时间定义:
通过调试: adb shell dumpsys input中查看窗口信息的dispatchingTimeout, 不同的窗口可能是不一样的, 这得看实际的时间设定, 如下3个值都设置为5秒, 那显示的都为5秒.
App如Launhcer:
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
//当前的超时设置为5's public static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
SystemUI的导航栏和状态栏:
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
/// // 这个时间关联到的是: // /// // Default input dispatching timeout in nanoseconds. static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L; h.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
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. constexpr std::chrono::nanoseconds DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s;
第二, 字符串定义(frameworks/base/core/res 目录下):
<string name="anr_activity_application" msgid="8121716632960340680">"<xliff:g id="APPLICATION">%2$s</xliff:g>没有响应"</string> <string name="anr_activity_process" msgid="3477362583767128667">"<xliff:g id="ACTIVITY">%1$s</xliff:g>没有响应"</string> <string name="anr_application_process" msgid="4978772139461676184">"<xliff:g id="APPLICATION">%1$s</xliff:g>没有响应"</string> <string name="anr_process" msgid="1664277165911816067">"进程“<xliff:g id="PROCESS">%1$s</xliff:g>”没有响应"</string> <string name="force_close" msgid="9035203496368973803">"确定"</string> <string name="report" msgid="2149194372340349521">"报告"</string> <string name="wait" msgid="7765985809494033348">"等待"</string>
时序
关键代码
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) { sp<IBinder> token = commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr; mLock.unlock(); const nsecs_t timeoutExtension = mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason); mLock.lock(); if (timeoutExtension > 0) { extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension); } else { // stop waking up for events in this connection, it is already not responding sp<Connection> connection = getConnectionLocked(token); if (connection == nullptr) { return; } cancelEventsForAnrLocked(connection); } } void InputDispatcher::onAnrLocked(const sp<Connection>& connection) { // Since we are allowing the policy to extend the timeout, maybe the waitQueue // is already healthy again. Don't raise ANR in this situation if (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()); updateLastAnrStateLocked(getWindowHandleLocked(connection->inputChannel->getConnectionToken()), reason); std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible); commandEntry->inputApplicationHandle = nullptr; commandEntry->inputChannel = connection->inputChannel; commandEntry->reason = std::move(reason); postCommandLocked(std::move(commandEntry)); } /** * 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 = LONG_LONG_MAX; // Check if we are waiting for a focused window to appear. Raise ANR if waited too long if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) { if (currentTime >= *mNoFocusedWindowTimeoutTime) { onAnrLocked(mAwaitedFocusedApplication); mAwaitedFocusedApplication.clear(); return LONG_LONG_MIN; } else { // Keep waiting const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime); ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining); nextAnrCheck = *mNoFocusedWindowTimeoutTime; } } // Check if any connection ANRs are due nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout()); if (currentTime < nextAnrCheck) { // most likely scenario return nextAnrCheck; // everything is normal. Let's check again at nextAnrCheck } // If we reached here, we have an unresponsive 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 mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); //ANR触发位置 onAnrLocked(connection); return LONG_LONG_MIN; } void InputDispatcher::dispatchOnce() { nsecs_t nextWakeupTime = LONG_LONG_MAX; { // acquire lock std::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 (runCommandsLockedInterruptible()) { nextWakeupTime = LONG_LONG_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 events if (nextWakeupTime == LONG_LONG_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); }
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
nsecs_t NativeInputManager::notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle, const sp<IBinder>& token, const std::string& reason) { #if DEBUG_INPUT_DISPATCHER_POLICY ALOGD("notifyANR"); #endif ATRACE_CALL(); JNIEnv* env = jniEnv(); ScopedLocalFrame localFrame(env); jobject inputApplicationHandleObj = getInputApplicationHandleObjLocalRef(env, inputApplicationHandle); jobject tokenObj = javaObjectForIBinder(env, token); jstring reasonObj = env->NewStringUTF(reason.c_str()); jlong newTimeout = env->CallLongMethod(mServiceObj, gServiceClassInfo.notifyANR, inputApplicationHandleObj, tokenObj, reasonObj); if (checkAndClearExceptionFromCallback(env, "notifyANR")) { newTimeout = 0; // abort dispatch } else { assert(newTimeout >= 0); } return newTimeout; } GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz, "notifyANR", "(Landroid/view/InputApplicationHandle;Landroid/os/IBinder;Ljava/lang/String;)J");
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
// Native callback. private long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token, String reason) { return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, token, reason); }
frameworks/base/services/core/java/com/android/server/wm/InputManagerCallback.java
/** * Notifies the window manager about an application that is not responding. * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch. * * Called by the InputManager. */ @Override public long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token, String reason) { final long startTime = SystemClock.uptimeMillis(); try { return notifyANRInner(inputApplicationHandle, token, reason); } finally { // Log the time because the method is called from InputDispatcher thread. It shouldn't // take too long that may affect input response time. Slog.d(TAG_WM, "notifyANR took " + (SystemClock.uptimeMillis() - startTime) + "ms"); } } private long notifyANRInner(InputApplicationHandle inputApplicationHandle, IBinder token, String reason) { ActivityRecord activity = null; WindowState windowState = null; boolean aboveSystem = false; int windowPid = INVALID_PID; preDumpIfLockTooSlow(); //TODO(b/141764879) Limit scope of wm lock when input calls notifyANR synchronized (mService.mGlobalLock) { // Check if we can blame a window if (token != null) { windowState = mService.mInputToWindowMap.get(token); if (windowState != null) { activity = windowState.mActivityRecord; windowPid = windowState.mSession.mPid; // Figure out whether this window is layered above system windows. // We need to do this here to help the activity manager know how to // layer its ANR dialog. aboveSystem = isWindowAboveSystem(windowState); } } // Check if we can blame an embedded window if (token != null && windowState == null) { EmbeddedWindow embeddedWindow = mService.mEmbeddedWindowController.get(token); if (embeddedWindow != null) { windowPid = embeddedWindow.mOwnerPid; WindowState hostWindowState = embeddedWindow.mHostWindowState; if (hostWindowState == null) { // The embedded window has no host window and we cannot easily determine // its z order. Try to place the anr dialog as high as possible. aboveSystem = true; } else { aboveSystem = isWindowAboveSystem(hostWindowState); } } } // Check if we can blame an activity. If we don't have an activity to blame, pull out // the token passed in via input application handle. This can happen if there are no // focused windows but input dispatcher knows the focused app. if (activity == null && inputApplicationHandle != null) { activity = ActivityRecord.forTokenLocked(inputApplicationHandle.token); } if (windowState != null) { Slog.i(TAG_WM, "Input event dispatching timed out " + "sending to " + windowState.mAttrs.getTitle() + ". Reason: " + reason); } else if (activity != null) { Slog.i(TAG_WM, "Input event dispatching timed out " + "sending to application " + activity.stringName + ". Reason: " + reason); } else { Slog.i(TAG_WM, "Input event dispatching timed out " + ". Reason: " + reason); } mService.saveANRStateLocked(activity, windowState, reason); } // All the calls below need to happen without the WM lock held since they call into AM. mService.mAtmInternal.saveANRState(reason); if (activity != null && activity.appToken != null) { // Notify the activity manager about the timeout and let it decide whether // to abort dispatching or keep waiting. final boolean abort = activity.keyDispatchingTimedOut(reason, windowPid); if (!abort) { // The activity manager declined to abort dispatching. // Wait a bit longer and timeout again later. return activity.mInputDispatchingTimeoutNanos; } } else if (windowState != null || windowPid != INVALID_PID) { // Notify the activity manager about the timeout and let it decide whether // to abort dispatching or keep waiting. long timeout = mService.mAmInternal.inputDispatchingTimedOut(windowPid, aboveSystem, reason); if (timeout >= 0) { // The activity manager declined to abort dispatching. // Wait a bit longer and timeout again later. return timeout * 1000000L; // nanoseconds } } return 0; // abort dispatching }
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
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; long timeout; synchronized (this) { synchronized (mPidsSelfLocked) { proc = mPidsSelfLocked.get(pid); } timeout = proc != null ? proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS; } if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) { return -1; } return timeout; } final class UiHandler extends Handler { //Ignored.... @Override public void handleMessage(Message msg) { switch (msg.what) { case SHOW_ERROR_UI_MSG: { mAppErrors.handleShowAppErrorUi(msg); ensureBootCompleted(); } break; case SHOW_NOT_RESPONDING_UI_MSG: { mAppErrors.handleShowAnrUi(msg); ensureBootCompleted(); } break; //Ignored ..... }
frameworks/base/services/core/java/com/android/server/am/AnrHelper.java
void appNotResponding(ProcessRecord anrProcess, String annotation) { appNotResponding(anrProcess, null /* activityShortComponentName */, null /* aInfo */, null /* parentShortComponentName */, null /* parentProcess */, false /* aboveSystem */, annotation); } 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(); } private void startAnrConsumerIfNeeded() { if (mRunning.compareAndSet(false, true)) { new AnrConsumerThread().start(); } }
frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo, String parentShortComponentName, WindowProcessController parentProcess, boolean aboveSystem, String annotation, boolean onlyDumpSelf) { //Ignored... // 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 dialog Message msg = Message.obtain(); msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG; msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem); mService.mUiHandler.sendMessage(msg); } } } class ErrorDialogControlle //AppErrors 调用 void showAnrDialogs(AppNotRespondingDialog.Data data) { List<Context> contexts = getDisplayContexts(isSilentAnr() /* lastUsedOnly */); mAnrDialogs = new ArrayList<>(); for (int i = contexts.size() - 1; i >= 0; i--) { final Context c = contexts.get(i); mAnrDialogs.add(new AppNotRespondingDialog(mService, c, data)); } mService.mUiHandler.post(() -> { List<AppNotRespondingDialog> dialogs; synchronized (mService) { dialogs = mAnrDialogs; } if (dialogs != null) { forAllDialogs(dialogs, Dialog::show); } }); }
frameworks/base/services/core/java/com/android/server/am/AppErrors.java
void handleShowAnrUi(Message msg) { List<VersionedPackage> packageList = null; synchronized (mService) { AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj; final ProcessRecord proc = data.proc; if (proc == null) { Slog.e(TAG, "handleShowAnrUi: proc is null"); return; } if (!proc.isPersistent()) { packageList = proc.getPackageListWithVersionCode(); } if (proc.getDialogController().hasAnrDialogs()) { Slog.e(TAG, "App already has anr dialog: " + proc); MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR, AppNotRespondingDialog.ALREADY_SHOWING); return; } boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) { proc.getDialogController().showAnrDialogs(data); //Ignored ..... }
frameworks/base/services/core/java/com/android/server/am/AppNotRespondingDialog.java
对话框的代码就省略吧.
至此, 显示ANR对话框的流程走完
InputDispatcher中窗口句柄
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::updateInputFlinger() { ATRACE_CALL(); if (!mInputFlinger) { return; } if (mVisibleRegionsDirty || mInputInfoChanged) { mInputInfoChanged = false; updateInputWindowInfo(); } else if (mInputWindowCommands.syncInputWindows) { // If the caller requested to sync input windows, but there are no // changes to input windows, notify immediately. setInputWindowsFinished(); } mInputWindowCommands.clear(); } void SurfaceFlinger::updateInputWindowInfo() { std::vector<InputWindowInfo> inputHandles; mDrawingState.traverseInReverseZOrder([&](Layer* layer) { if (layer->needsInputInfo()) { // When calculating the screen bounds we ignore the transparent region since it may // result in an unwanted offset. inputHandles.push_back(layer->fillInputInfo()); } }); mInputFlinger->setInputWindows(inputHandles, mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener : nullptr); }
frameworks/native/services/inputflinger/InputManager.cpp
void InputManager::setInputWindows(const std::vector<InputWindowInfo>& infos, const sp<ISetInputWindowsListener>& setInputWindowsListener) { std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> handlesPerDisplay; std::vector<sp<InputWindowHandle>> handles; for (const auto& info : infos) { handlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>()); handlesPerDisplay[info.displayId].push_back(new BinderWindowHandle(info)); } mDispatcher->setInputWindows(handlesPerDisplay); if (setInputWindowsListener) { setInputWindowsListener->onSetInputWindowsFinished(); } }
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
/// // 通过InputManagerService 添加窗口句柄 // /// /** * Called from InputManagerService, update window handle list by displayId that can receive input. * A window handle contains information about InputChannel, Touch Region, Types, Focused,... * If set an empty list, remove all handles from the specific display. * For focused handle, check if need to change and send a cancel event to previous one. * For removed handle, check if need to send a cancel event if already in touch. */ void InputDispatcher::setInputWindowsLocked(){ updateWindowHandlesForDisplayLocked... } /// // 更新mWindowHandlesByDisplay中 // /// void InputDispatcher::updateWindowHandlesForDisplayLocked( const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) { //Ignored..... // Insert or replace mWindowHandlesByDisplay[displayId] = newHandles; } /// // 从mWindowHandlesByDisplay中查询当前窗口 // /// sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked( const sp<IBinder>& windowHandleToken) const { if (windowHandleToken == nullptr) { return nullptr; } for (auto& it : mWindowHandlesByDisplay) { const std::vector<sp<InputWindowHandle>> windowHandles = it.second; for (const sp<InputWindowHandle>& windowHandle : windowHandles) { if (windowHandle->getToken() == windowHandleToken) { return windowHandle; } } } return nullptr; } nsecs_t InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) { sp<InputWindowHandle> window = getWindowHandleLocked(token); if (window != nullptr) { return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT).count(); } return DEFAULT_INPUT_DISPATCHING_TIMEOUT.count(); }
frameworks/base/services/core/java/com/android/server/wm/WindowProcessController.java
public long getInputDispatchingTimeout() { synchronized (mAtm.mGlobalLock) { return isInstrumenting() || isUsingWrapper() ? INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS : KEY_DISPATCHING_TIMEOUT_MS; } }
frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
public long getInputDispatchingTimeout() { return mWindowProcessController.getInputDispatchingTimeout(); }
扩展
编译及替换
#编译 mmm frameworks/native/services/inputflinger/ -j2 #替换 adb push out/target/product/rk3566_r/system/bin/inputflinger /system/bin/ adb push out/target/product/rk3566_r/system/lib/libinputflinger.so /system/lib/ adb push out/target/product/rk3566_r/system/lib64/libinputflinger.so /system/lib64/
调试: adb shell dumpsys input
0: name='Window{57ce99a u0 NavigationBar0}', displayId=0, portalToDisplayId=-1, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x21840068, type=0x000007e3, frame=[0,752][1280,800], globalScale=1.000000, windowScale=(1.000000,1.000000), touchableRegion=[0,752][1280,800], inputFeatures=0x00000000, ownerPid=614, ownerUid=10119, dispatchingTimeout=7000ms 1: name='Window{61c1033 u0 StatusBar}', displayId=0, portalToDisplayId=-1, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x81800408, type=0x000007d0, frame=[0,0][1280,24], globalScale=1.000000, windowScale=(1.000000,1.000000), touchableRegion=[0,0][1280,24], inputFeatures=0x00000000, ownerPid=614, ownerUid=10119, dispatchingTimeout=7000ms 2: name='Window{e0e47c4 u0 com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher}', displayId=0, portalToDisplayId=-1, paused=false, hasFocus=true, hasWallpaper=true, visible=true, canReceiveKeys=true, flags=0x81910120, type=0x00000001, frame=[0,0][1280,800], globalScale=1.000000, windowScale=(1.000000,1.000000), touchableRegion=[0,0][1280,800], inputFeatures=0x00000000, ownerPid=1039, ownerUid=10121, dispatchingTimeout=15000ms 3: name='Window{bbadc9c u0 com.android.systemui.ImageWallpaper}', displayId=0, portalToDisplayId=-1, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x00014318, type=0x000007dd, frame=[-64,-304][1344,1104], globalScale=1.000000, windowScale=(0.909091,0.909091), touchableRegion=[-64,-304][1344,1104], inputFeatures=0x00000000, ownerPid=614, ownerUid=10119, dispatchingTimeout=7000ms
参考
ANR
Android ANR:原理分析及解决办法