欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

ANR应用:无焦点窗口处理流程解析

最编程 2024-02-03 09:53:51
...

最近在项目上处理比较多的应用No Focused Window类型的问题,于是把流程梳理一遍(基于Android R),了解问题发生的原因才好有相应的应对措施。

首先看No Focused Window是什么样的表现,通常是应用ANR时的reason是:

  • Input dispatching timed out sending to application ActivityRecord{xxx}. ActivityRecord{xxx} does not have a focused window

还有一种类型如下:

  • Appilcation is not responding. waited 5000ms for ActivityRecord{xxx}

本文重点讲xxx does not have a focused window的流程,也会提到什么情况下会出现waited 5000ms for xxx

首先要知道的是,这两个类型的ANR都是InputDispatcher触发的,所以要从input事件分发的流程去看触发ANR的过程。当有input事件过来的时候,就会触发到InputDispatcherdispatchOnce(),相关代码如下,关键流程有相关注释。

// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        std::scoped_lock _l(mLock);
        mDispatcherIsAlive.notify_all();

        // 判断mCommandQueue是否为空
        // mCommandQueue是通过postCommandLocked()添加元素
        // postCommandLocked()在多处地方调用,总结来说就是有事件触发时mCommandQueue不会为空
        // onFocusChangedLocked() -> postCommandLocked()
        // onAnrLocked() -> postCommandLocked()
        // dispatchConfigurationChangedLocked() -> postCommandLocked()
        // dispatchKeyLocked() -> postCommandLocked()
        if (!haveCommandsLocked()) {
            // 一般来说会走到dispatchOnceInnerLocked流程
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        // 执行mCommandQueue中的命令,如果mCommandQueue为空返回false,有命令则为true
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }

        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);
}

一般来说会走到dispatchOnceInnerLocked()这个流程,相关代码如下,关键流程有注释。

// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();
    // ......
    // mPendingEvent默认为空,会从mInboundQueue中获取到值
    if (!mPendingEvent) {
        // mInboundQueue是通过enqueueInboundEventLocked()赋值,所以有事件的时候mInboundQueue不为空
        // notifyConfigurationChanged() -> enqueueInboundEventLocked()
        // notifyKey() -> enqueueInboundEventLocked()
        // notifyMotion() -> enqueueInboundEventLocked()
        // notifyDeviceReset() -> enqueueInboundEventLocked()
        // injectInputEvent() -> enqueueInboundEventLocked()
        // setInputWindowsLocked() -> enqueueFocusEventLocked()
        if (mInboundQueue.empty()) {
            // ......
            // 如果mInboundQueue也没有pending的事件,直接返回
            if (!mPendingEvent) {
                return;
            }
        } else {
            // 一般走这个流程,取mInboundQueue第一个元素
            mPendingEvent = mInboundQueue.front();
            mInboundQueue.pop_front();
            traceInboundQueueLengthLocked();
        }
        // ......
    }

    // 走到这个地方,mPendingEvent一般不会为空
    ALOG_ASSERT(mPendingEvent != nullptr);
    bool done = false;
    DropReason dropReason = DropReason::NOT_DROPPED;
    // ......
    // 根据pendingEvent的类型创建不同的实例去分发事件
    // 不同的入口的类型不一样,如调用了notifyKey()就会创建一个KeyEntry
    switch (mPendingEvent->type) {
        // 来源:notifyConfigurationChanged() -> new ConfigurationChangedEntry()
        case EventEntry::Type::CONFIGURATION_CHANGED: {
            ConfigurationChangedEntry* typedEntry =
                    static_cast<ConfigurationChangedEntry*>(mPendingEvent);
            done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
            dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped
            break;
        }
        // 来源:notifyDeviceReset() -> new DeviceResetEntry()
        case EventEntry::Type::DEVICE_RESET: {
            DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent);
            done = dispatchDeviceResetLocked(currentTime, typedEntry);
            dropReason = DropReason::NOT_DROPPED; // device resets are never dropped
            break;
        }
        // 来源:setInputWindowsLocked() -> enqueueFocusEventLocked() -> new FocusEntry()
        case EventEntry::Type::FOCUS: {
            FocusEntry* typedEntry = static_cast<FocusEntry*>(mPendingEvent);
            dispatchFocusLocked(currentTime, typedEntry);
            done = true;
            dropReason = DropReason::NOT_DROPPED; // focus events are never dropped
            break;
        }
        // 来源:notifyKey() -> new KeyEntry()
        case EventEntry::Type::KEY: {
            KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
            // ......
            // 跳转到findFocusedWindowTargetsLocked() -> dispatchEventLocked()
            done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
            break;
        }
        // 来源:notifyMotion() -> new MotionEntry()
        case EventEntry::Type::MOTION: {
            MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
            // ......
            // 跳转到findFocusedWindowTargetsLocked() -> dispatchEventLocked()
            done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
            break;
        }
    }

    // ......
}

前面处理按键Key事件(dispatchKeyLocked)或者触摸Motion事件(dispatchMotionLocked)都是需要找到焦点窗口去处理,都会调用到findFocusedWindowTargetsLocked()findFocusedWindowTargetsLocked()流程如下,关键步骤有注释。

// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
                                                        const EventEntry& entry,
                                                        std::vector<InputTarget>& inputTargets,
                                                        nsecs_t* nextWakeupTime) {
    std::string reason;

    int32_t displayId = getTargetDisplayId(entry);
    // mFocusedWindowHandlesByDisplay在setInputWindowsLocked()里赋值
    sp<InputWindowHandle> focusedWindowHandle =
            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
    // mFocusedApplicationHandlesByDisplay在setFocusedApplication()里赋值
    sp<InputApplicationHandle> focusedApplicationHandle =
            getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);

    // focusedWindowHandle和focusedApplicationHandle都为空时表示当前无窗口,该事件会被丢弃,不会执行dispatchEventLocked
    // 一般出现两个都为空的场景,是在窗口切换的过程,此时不处理事件注入
    if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
        ALOGI("Dropping %s event because there is no focused window or focused application in "
              "display %" PRId32 ".",
              EventEntry::typeToString(entry.type), displayId);
        return INPUT_EVENT_INJECTION_FAILED;
    }

    // focusedWindowHandle为空但focusedApplicationHandle不为空时开始ANR检查
    if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
        // 默认mNoFocusedWindowTimeoutTime没有值,第一次检查ANR会走下面这个流程
        if (!mNoFocusedWindowTimeoutTime.has_value()) {
            // DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s * HwTimeoutMultiplier();
            // 默认input dispatch timeout时间时5s
            const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout(
                    DEFAULT_INPUT_DISPATCHING_TIMEOUT.count());
            // 给mNoFocusedWindowTimeoutTime赋值,触发ANR时会检查这个值是否为空,不为空才触发ANR
            mNoFocusedWindowTimeoutTime = currentTime + timeout;
            // 把当前的focusedApplicationHandle赋值给mAwaitedFocusedApplication,触发ANR时会检查这个值是否为空,不为空才触发ANR
            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(), ns2ms(timeout));
            *nextWakeupTime = *mNoFocusedWindowTimeoutTime;
            // 返回INPUT_EVENT_INJECTION_PENDING表示dispatchKeyLocked()或者dispatchMotionLocked()为false
            return INPUT_EVENT_INJECTION_PENDING;
        } else if (currentTime > *mNoFocusedWindowTimeoutTime) {
            // Already raised ANR. Drop the event
            ALOGE("Dropping %s event because there is no focused window",
                  EventEntry::typeToString(entry.type));
            return INPUT_EVENT_INJECTION_FAILED;
        } else {
            // Still waiting for the focused window
            return INPUT_EVENT_INJECTION_PENDING;
        }
    }

    // 如果走到这个流程,说明没有ANR,清空mNoFocusedWindowTimeoutTime和mAwaitedFocusedApplication
    resetNoFocusedWindowTimeoutLocked();
    // ......
    // Done.
    return INPUT_EVENT_INJECTION_SUCCEEDED;
}

前面看到,是否触发ANR跟focusedWindowHandlefocusedApplicationHandle有关,那么这两个值的赋值流程是在哪里呢?

跟踪代码流程,可以看到,这两个流程都是WindowManagerService在窗口切换的过程中,设置到InputDispatcher中的,最后调用setInputWindowsLocked()设置focusedWindowHandle,调用setFocusedApplication()设置focusedApplicationHandle,相关流程如下,关键步骤有注释。

setInputWindow.PNG
// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
// 当VSYNC信号来了之后,会调用到SurfaceFlinger的onMessageInvalidate()方法
// SurfaceFlinger::onMessageInvalidate() 
//   ==> SurfaceFlinger: updateInputFlinger()
//    ==> SurfaceFlinger: updateInputWindowInfo()
//      ==> InputManager::setInputWindows()
//        ==> InputDispatcher::setInputWindows()
//          ==> InputDispatcher::setInputWindowsLocked()
void InputDispatcher::setInputWindowsLocked(
        const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) {

    // ......
    const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
    // 更新mWindowHandlesByDisplay这个map,然后通过getWindowHandlesLocked()找newFocusedWindowHandle
    updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);

    sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
    bool foundHoveredWindow = false;
    // 在mWindowHandlesByDisplay这个map里面找newFocusedWindowHandle
    for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
        // newFocusedWindowHandle要不为空,windowHandle具备focusable和visible属性
        if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus &&
            windowHandle->getInfo()->visible) {
            // 给newFocusedWindowHandle赋值,最后这个值存到mFocusedWindowHandlesByDisplay这个map
            newFocusedWindowHandle = windowHandle;
        }
        if (windowHandle == mLastHoverWindowHandle) {
            foundHoveredWindow = true;
        }
    }

    if (!foundHoveredWindow) {
        mLastHoverWindowHandle = nullptr;
    }

    // 在mFocusedWindowHandlesByDisplay这个map里找当前的焦点窗口
    sp<InputWindowHandle> oldFocusedWindowHandle =
            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);

    // 判断oldFocusedWindowHandle是否等于newFocusedWindowHandle,如果相等则不走focus change流程
    if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
        // 如果当前的焦点窗口不为空,需要从mFocusedWindowHandlesByDisplay移除掉
        if (oldFocusedWindowHandle != nullptr) {
            sp<InputChannel> focusedInputChannel =
                    getInputChannelLocked(oldFocusedWindowHandle->getToken());
            if (focusedInputChannel != nullptr) {
                CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                                           "focus left window");
                synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
                // 新建一个FocusEntry加入到mInboundQueue去dispatch
                enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
            }
            // oldFocusedWindowHandle不为空时需要移除旧的
            mFocusedWindowHandlesByDisplay.erase(displayId);
        }
        // 走到这个流程,如果oldFocusedWindowHandle不为空,newFocusedWindowHandle为空,那么在findFocusedWindowTargetsLocked()中的focusedWindowHandle为空
        // 如果newFocusedWindowHandle不为空,更新mFocusedWindowHandlesByDisplay
        if (newFocusedWindowHandle != nullptr) {
            // 更新mFocusedWindowHandlesByDisplay,在findFocusedWindowTargetsLocked()时用到
            mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
            // 新建一个FocusEntry加入到mInboundQueue去dispatch
            enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/);
        }

        if (mFocusedDisplayId == displayId) {
            // 添加focusChanged到mCommandQueue,在dispatchOnce时会执行
            onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
        }
    }

    // ......
}

setFocusedApplication()流程如下:

setFocusedApplication.PNG
// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
void InputDispatcher::setFocusedApplication(
        int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) {
    { // acquire lock
        std::scoped_lock _l(mLock);
        // 获取当前的focusedApplicationHandle
        sp<InputApplicationHandle> oldFocusedApplicationHandle =
                getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
        // 如果当前的focusedApplicationHandle跟触发ANR是的focusedApplicationHandle是一样且
        // 新的focusedApplicationHandle跟旧的不一样,说明focusedApplicationHandle有更新
        // 需要重置ANR计时
        if (oldFocusedApplicationHandle == mAwaitedFocusedApplication &&
            inputApplicationHandle != oldFocusedApplicationHandle) {
            // 重置ANR计时
            resetNoFocusedWindowTimeoutLocked();
        }

        if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) {
            if (oldFocusedApplicationHandle != inputApplicationHandle) {
                // 赋值新的inputApplicationHandle到mFocusedApplicationHandlesByDisplay,在findFocusedWindowTargetsLocked()时用到
                mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
            }
        } else if (oldFocusedApplicationHandle != nullptr) {
            // 如果inputApplicationHandle为空,oldFocusedApplicationHandle不为空,需要清楚oldFocusedApplicationHandle
            oldFocusedApplicationHandle.clear();
            // 走到这个流程会出现findFocusedWindowTargetsLocked()中focusedApplicationHandle为空
            mFocusedApplicationHandlesByDisplay.erase(displayId);
        }
    } // release lock

    // Wake up poll loop since it may need to make new input dispatching choices.
    mLooper->wake();
}

dispatchOnce()处理完事件后,会走processAnrsLocked()检查是否有ANR发生。相关流程如下,关键流程有注释。

// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
nsecs_t InputDispatcher::processAnrsLocked() {
    const nsecs_t currentTime = now();
    nsecs_t nextAnrCheck = LONG_LONG_MAX;
    // 在findFocusedWindowTargetsLocked()中,如果focusedWindowHandle为空,focusedApplicationHandle不为空,以下条件就会满足
    if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
        // mNoFocusedWindowTimeoutTime为检查时间+5s,如果currentTime大于等于mNoFocusedWindowTimeoutTime,表示超时
        if (currentTime >= *mNoFocusedWindowTimeoutTime) {
            // 触发ANR流程,此处触发的ANR类型是xxx does not have a focused window
            processNoFocusedWindowAnrLocked();
            // 清空mAwaitedFocusedApplication,下次就不会再走ANR流程
            mAwaitedFocusedApplication.clear();
            mNoFocusedWindowTimeoutTime = std::nullopt;
            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;
        }
    }
    // 如果到这个流程,说明focusedWindowHandle不为空,或者是检查到了focusedWindowHandle为空,focusedApplicationHandle不为空但未超时
    // nextAnrCheck此时可能为mNoFocusedWindowTimeoutTime,也可能是LONG_LONG_MAX
    // mAnrTracker.firstTimeout()默认为max,在startDispatchCycleLocked()更新mAnrTracker.firstTimeout()
    // mAnrTracker.firstTimeout为startDispatchCycleLocked()调用时间+5s
    nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
    // currentTime小于nextAnrCheck表示未超时,等待下一次检查
    if (currentTime < nextAnrCheck) { // most likely scenario
        return nextAnrCheck;          // everything is normal. Let's check again at nextAnrCheck
    }
    // 如果走到这个流程,表示currentTime大于mNoFocusedWindowTimeoutTime或者是mAnrTracker.firstTimeout()
    // 场景应该是在触发xxx does not have a focused window类型ANR前超过了mAnrTracker.firstTimeout()
    sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
    if (connection == nullptr) {
        ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());
        return nextAnrCheck;
    }
    // 将responsive置为false,避免更新mAnrTracker
    connection->responsive = false;
    // 擦除当前token的ANR记录并触发ANR
    mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
    // 如果走到这个流程,ANR类型是xxx is not responding. Waited xxx ms for xxx
    // 这个地方,focusedWindowHandle和focusedApplicationHandle都是不为空的场景
    onAnrLocked(*connection);
    return LONG_LONG_MIN;
}

类型是xxx does not have a focused windowprocessNoFocusedWindowAnrLocked()流程,最后也是会走到onAnrLocked(),最后执行doNotifyAnrLockedInterruptible()processNoFocusedWindowAnrLocked()是google后续补上的拯救措施,一开始代码是不包含的,会导致ANR存在误判的场景。processNoFocusedWindowAnrLocked()流程如下,关键流程有注释。

// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::processNoFocusedWindowAnrLocked() {
    // 在触发ANR前,再获取一次当前的focusedApplication
    sp<InputApplicationHandle> focusedApplication =
            getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);
    // 检查触发ANR时的条件是focusedApplication不为空
    // 如果此时focusedApplication为空,或者focusedApplication不等于前一个mAwaitedFocusedApplication表示已经切换application focus,取消触发ANR
    if (focusedApplication == nullptr ||
        focusedApplication->getApplicationToken() !=
                mAwaitedFocusedApplication->getApplicationToken()) {
        // Unexpected because we should have reset the ANR timer when focused application changed
        ALOGE("Waited for a focused window, but focused application has already changed to %s",
              focusedApplication->getName().c_str());
        return; // The focused application has changed.
    }
    // 在触发ANR前,再获取一次当前的focusedWindowHandle 
    const sp<InputWindowHandle>& focusedWindowHandle =
            getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);
    // 检查触发ANR时focusedWindowHandle为空,如果此时focusedWindowHandle不为空,取消触发ANR
    if (focusedWindowHandle != nullptr) {
        return; // We now have a focused window. No need for ANR.
    }
    // 通过前面的判断,还是无法拦截,说明该ANR无可避免,最终触发ANR
    // 早期代码没有前面一系列的判断,是直接触发的ANR,会在性能较差的场景下出现误判
    onAnrLocked(mAwaitedFocusedApplication);
}

onAnrLocked()有两个实现,两个参数不一样,一个是传inputConnection,一个是传InputApplicationHandle。两者的ANR reason不一样,最终走的流程一样,都是走doNotifyAnrLockedInterruptible()触发ANR。

// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
// 从processNoFocusedWindowAnrLocked()走过来的流程
void InputDispatcher::onAnrLocked(const sp<InputApplicationHandle>& application) {
    // 这个流程走过来的ANR类型是xxx does not have a focused window
    std::string reason = android::base::StringPrintf("%s does not have a focused window",
                                                     application->getName().c_str());
    // 更新ANR信息
    updateLastAnrStateLocked(application, reason);

    // 构建CommandEntry,在dispatchOnce时执行命令
    std::unique_ptr<CommandEntry> commandEntry =
            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
    commandEntry->inputApplicationHandle = application;
    commandEntry->inputChannel = nullptr;
    commandEntry->reason = std::move(reason);
    // 发送命令到mCommandQueue,执行doNotifyAnrLockedInterruptible()
    postCommandLocked(std::move(commandEntry));
}

// 在processAnrsLocked()时没有走processNoFocusedWindowAnrLocked()后走到的流程
void InputDispatcher::onAnrLocked(const Connection& connection) {
    // 前面走了startDispatchCycleLocked()流程,waitQueue不为空,如果为空,说明已经正常处理,此时不触发ANR
    if (connection.waitQueue.empty()) {
        ALOGI("Not raising ANR because the connection %s has recovered",
              connection.inputChannel->getName().c_str());
        return;
    }

    DispatchEntry* oldestEntry = *connection.waitQueue.begin();
    const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
    // 这个流程的ANR类型是Application is not responding. Waited 5000ms for xxx
    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());
    // 更新ANR信息
    updateLastAnrStateLocked(getWindowHandleLocked(connection.inputChannel->getConnectionToken()),
                             reason);
    // 构建CommandEntry,在dispatchOnce时执行命令
    std::unique_ptr<CommandEntry> commandEntry =
            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
    commandEntry->inputApplicationHandle = nullptr;
    commandEntry->inputChannel = connection.inputChannel;
    commandEntry->reason = std::move(reason);
    // 发送命令到mCommandQueue,执行doNotifyAnrLockedInterruptible()
    postCommandLocked(std::move(commandEntry));
}

// 触发ANR时走到流程
void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) {
    sp<IBinder> token =
            commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
    mLock.unlock();
    // 通知system_server出现ANR
    const nsecs_t timeoutExtension =
            mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);

    mLock.lock();
    // 如果通知system_server出现ANR时出现一次,则timeoutExtension为0,表示ANR被abort,
    if (timeoutExtension > 0) {
        // 更新ANR相关信息,等待下一次ANR
        extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension);
    } else {
        // 已经触发ANR
        sp<Connection> connection = getConnectionLocked(token);
        if (connection == nullptr) {
            return;
        }
        // 已经出现ANR,停止对这个connection派发事件
        cancelEventsForAnrLocked(connection);
    }
}

通过JNI通知system_server出现了ANR,最后调到appNotResponding()方法

// 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());
    // call到InputManagerCallback的notifyANR()方法
    jlong newTimeout = env->CallLongMethod(mServiceObj,
            gServiceClassInfo.notifyANR, inputApplicationHandleObj, tokenObj,
                 reasonObj);
    // 如果出现异常,返回0给到InputDispatcher重新计时
    if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
        newTimeout = 0; // abort dispatch
    } else {
        assert(newTimeout >= 0);
    }
    return newTimeout;
}

system_server代码流程如下:

// frameworks/base/services/core/java/com/android/server/wm/InputManagerCallback.java
public long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token, String reason) {
    final long startTime = SystemClock.uptimeMillis();
    try {
        // notifyANRInner()
        //   ==> ActivityManagerService.inputDispatchingTimedOut()
        //     ==> ProcessRecord.appNotResponding()
        // 最终通过appNotResponding()弹出应用无响应的对话框
        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");
    }
}

结语:

理清整个流程后,要处理这类问题,主要是要分析setFocusedApplicationsetInputWindows流程有没有走到。其中的逻辑主要集中在WindowManagerService,涉及应用生命周期、窗口可见性变化等逻辑。应用端做一手分析时,需要确保生命周期走到了onResume,界面处于可见状态。在应用生命周期正常的情况下,系统侧分析就需要从多个状态去判断是否正常,其中很重要的一个方法是canReceiveKeys(),其中的逻辑涉及较多,就不在此文介绍了。

canReceiveKeys.png