美文网首页Android FrameworkAndroid开发之散记
应用ANR:No Focused Window流程分析

应用ANR:No Focused Window流程分析

作者: 逸先森 | 来源:发表于2021-08-22 17:35 被阅读0次

    最近在项目上处理比较多的应用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

    相关文章

      网友评论

        本文标题:应用ANR:No Focused Window流程分析

        本文链接:https://www.haomeiwen.com/subject/plgciltx.html