Android14 InputManager-ANR原理

2024-02-22 09:12

本文主要是介绍Android14 InputManager-ANR原理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目标窗口查找时,作为派发目标的窗口必须已经准备好接收新的输入事件,否则判定窗口处于未响应状态,终止事件的派发过程,并在一段时间后再试。倘若5s后窗口仍然未准备好接收输入事件,将导致ANR。直接引发ANR的原因有很多,

例如Activity生命周期函数调用超时,

服务启动超时

以及最常见的输入事件处理超时等。

  1. Service ANR:前台20s,后台200s;startForeground超时10s
  2. Broadcast ANR:前台10s,后台60s
  3. Input ANR:按键或触摸事件在5s内无响应
  4. ContentProvider ANR:10s,少见

下面从输入事件超时的角度讨论ANR的产生原因与过程

inputANR 分为两种

无响应anr: 应用连接正常但是未相应事件,并且发生了超时

无焦点窗口anr: 当前有焦点应用,但是无焦点窗口,并且超时

void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LLONG_MAX;{ // acquire lockstd::scoped_lock _l(mLock);mDispatcherIsAlive.notify_all();// Run a dispatch loop if there are no pending commands.// The dispatch loop might enqueue commands to run afterwards.if (!haveCommandsLocked()) {dispatchOnceInnerLocked(&nextWakeupTime);}// Run all pending commands if there are any.// If any commands were run then force the next poll to wake up immediately.if (runCommandsLockedInterruptable()) {nextWakeupTime = LLONG_MIN;}// If we are still waiting for ack on some events,// we might have to wake up earlier to check if an app is anr'ing.const nsecs_t nextAnrCheck = processAnrsLocked();nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);// We are about to enter an infinitely long sleep, because we have no commands or// pending or queued eventsif (nextWakeupTime == LLONG_MAX) {mDispatcherEnteredIdle.notify_all();}} // release lock// Wait for callback or timeout or wake.  (make sure we round up, not down)nsecs_t currentTime = now();int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);mLooper->pollOnce(timeoutMillis);
}
nsecs_t InputDispatcher::processAnrsLocked() {const nsecs_t currentTime = now();nsecs_t nextAnrCheck = LLONG_MAX;// Check if we are waiting for a focused window to appear. Raise ANR if waited too longif (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {if (currentTime >= *mNoFocusedWindowTimeoutTime) {processNoFocusedWindowAnrLocked();// 无焦点anrmAwaitedFocusedApplication.reset();mNoFocusedWindowTimeoutTime = std::nullopt;return LLONG_MIN;} else {// Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes.nextAnrCheck = *mNoFocusedWindowTimeoutTime;}}// Check if any connection ANRs are duenextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());if (currentTime < nextAnrCheck) { // most likely scenarioreturn nextAnrCheck;          // everything is normal. Let's check again at nextAnrCheck}// If we reached here, we have an unresponsive connection.std::shared_ptr<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());if (connection == nullptr) {ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());return nextAnrCheck;}connection->responsive = false;// Stop waking up for this unresponsive connectionmAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());onAnrLocked(connection);// 无响应anrreturn LLONG_MIN;
}

mNoFocusedWindowTimeoutTime 和 mAwaitedFocusedApplication 的设置是在 InputDispatcher::findFocusedWindowTargetLocked

函数中:

sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked(nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,InputEventInjectionResult& outInjectionResult) {std::string reason;outInjectionResult = InputEventInjectionResult::FAILED; // Default resultint32_t displayId = getTargetDisplayId(entry);sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);// If there is no currently focused window and no focused application// then drop the event.if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {ALOGI("Dropping %s event because there is no focused window or focused application in ""display %" PRId32 ".",ftl::enum_string(entry.type).c_str(), displayId);return nullptr;}// Drop key events if requested by input featureif (focusedWindowHandle != nullptr && shouldDropInput(entry, focusedWindowHandle)) {return nullptr;}// Compatibility behavior: raise ANR if there is a focused application, but no focused window.// Only start counting when we have a focused event to dispatch. The ANR is canceled if we// start interacting with another application via touch (app switch). This code can be removed// if the "no focused window ANR" is moved to the policy. Input doesn't know whether// an app is expected to have a focused window.if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {if (!mNoFocusedWindowTimeoutTime.has_value()) {// We just discovered that there's no focused window. Start the ANR timerstd::chrono::nanoseconds timeout = focusedApplicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);mNoFocusedWindowTimeoutTime = currentTime + timeout.count();mAwaitedFocusedApplication = focusedApplicationHandle;mAwaitedApplicationDisplayId = displayId;ALOGW("Waiting because no window has focus but %s may eventually add a ""window when it finishes starting up. Will wait for %" PRId64 "ms",mAwaitedFocusedApplication->getName().c_str(), millis(timeout));*nextWakeupTime = *mNoFocusedWindowTimeoutTime;outInjectionResult = InputEventInjectionResult::PENDING;return nullptr;} else if (currentTime > *mNoFocusedWindowTimeoutTime) {// Already raised ANR. Drop the eventALOGE("Dropping %s event because there is no focused window",ftl::enum_string(entry.type).c_str());return nullptr;} else {// Still waiting for the focused windowoutInjectionResult = InputEventInjectionResult::PENDING;return nullptr;}}// we have a valid, non-null focused windowresetNoFocusedWindowTimeoutLocked();// Verify targeted injection.if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) {ALOGW("Dropping injected event: %s", (*err).c_str());outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;return nullptr;}if (focusedWindowHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) {ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());outInjectionResult = InputEventInjectionResult::PENDING;return nullptr;}// If the event is a key event, then we must wait for all previous events to// complete before delivering it because previous events may have the// side-effect of transferring focus to a different window and we want to// ensure that the following keys are sent to the new window.//// Suppose the user touches a button in a window then immediately presses "A".// If the button causes a pop-up window to appear then we want to ensure that// the "A" key is delivered to the new pop-up window.  This is because users// often anticipate pending UI changes when typing on a keyboard.// To obtain this behavior, we must serialize key events with respect to all// prior input events.if (entry.type == EventEntry::Type::KEY) {if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {*nextWakeupTime = *mKeyIsWaitingForEventsTimeout;outInjectionResult = InputEventInjectionResult::PENDING;return nullptr;}}outInjectionResult = InputEventInjectionResult::SUCCEEDED;return focusedWindowHandle;
}

可以看出引发 无焦点ANR 的两个条件:
1.mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr 并且 currentTime >= *mNoFocusedWindowTimeoutTime

mNoFocusedWindowTimeoutTime 和 mAwaitedFocusedApplication 的设置是在 InputDispatcher::findFocusedWindowTargetsLocked 函数中:

当有焦点应用但没有焦点窗口,设置 mNoFocusedWindowTimeoutTime 的值,开启 ANR 倒计时。对于当前事件返回 PENDING 。
接着在当前的派发循环中执行 processAnrsLocked 函数判断是否引发 ANR。

重置 mNoFocusedWindowTimeoutTime 和 mAwaitedFocusedApplication 是在 InputDispatcher::resetNoFocusedWindowTimeoutLocked 中。

执行 resetNoFocusedWindowTimeoutLocked 的几个时机:
(1)InputDispatcher::findFocusedWindowTargetLocked中,当等待有焦点窗口后;

(2)InputDispatcher::setFocusedApplication 中,切换焦点应用后;

(3)InputDispatcher::setInputDispatchMode 中;

(4) InputDispatcher::resetAndDropEverythingLocked

无焦点anr

void InputDispatcher::processNoFocusedWindowAnrLocked() {// Check if the application that we are waiting for is still focused.std::shared_ptr<InputApplicationHandle> focusedApplication =getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);if (focusedApplication == nullptr ||focusedApplication->getApplicationToken() !=mAwaitedFocusedApplication->getApplicationToken()) {// Unexpected because we should have reset the ANR timer when focused application changedALOGE("Waited for a focused window, but focused application has already changed to %s",focusedApplication->getName().c_str());return; // The focused application has changed.}const sp<WindowInfoHandle>& focusedWindowHandle =getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);if (focusedWindowHandle != nullptr) {return; // We now have a focused window. No need for ANR.}onAnrLocked(mAwaitedFocusedApplication);
}
void InputDispatcher::onAnrLocked(std::shared_ptr<InputApplicationHandle> application) {std::string reason =StringPrintf("%s does not have a focused window", application->getName().c_str());updateLastAnrStateLocked(*application, reason);auto command = [this, application = std::move(application)]() REQUIRES(mLock) {scoped_unlock unlock(mLock);mPolicy.notifyNoFocusedWindowAnr(application);};postCommandLocked(std::move(command));
}

 无响应anr


void InputDispatcher::onAnrLocked(const std::shared_ptr<Connection>& connection) {if (connection == nullptr) {LOG_ALWAYS_FATAL("Caller must check for nullness");}// Since we are allowing the policy to extend the timeout, maybe the waitQueue// is already healthy again. Don't raise ANR in this situationif (connection->waitQueue.empty()) {ALOGI("Not raising ANR because the connection %s has recovered",connection->inputChannel->getName().c_str());return;}/*** The "oldestEntry" is the entry that was first sent to the application. That entry, however,* may not be the one that caused the timeout to occur. One possibility is that window timeout* has changed. This could cause newer entries to time out before the already dispatched* entries. In that situation, the newest entries caused ANR. But in all likelihood, the app* processes the events linearly. So providing information about the oldest entry seems to be* most useful.*/DispatchEntry* oldestEntry = *connection->waitQueue.begin();const nsecs_t currentWait = now() - oldestEntry->deliveryTime;std::string reason =android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",connection->inputChannel->getName().c_str(),ns2ms(currentWait),oldestEntry->eventEntry->getDescription().c_str());sp<IBinder> connectionToken = connection->inputChannel->getConnectionToken();updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);//处理无响应anr
processConnectionUnresponsiveLocked(*connection, std::move(reason));
// cnnel所有事件// Stop waking up for events on this connection, it is already unresponsivecancelEventsForAnrLocked(connection);
}
void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection,std::string reason) {const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken();std::optional<int32_t> pid;if (connection.monitor) {ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(),reason.c_str());pid = findMonitorPidByTokenLocked(connectionToken);} else {// The connection is a windowALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(),reason.c_str());const sp<WindowInfoHandle> handle = getWindowHandleLocked(connectionToken);if (handle != nullptr) {pid = handle->getInfo()->ownerPid;}}
//发送窗口无响应命令sendWindowUnresponsiveCommandLocked(connectionToken, pid, std::move(reason));
}
//当这个command被执行时,调用的为doNotifyWindowUnresponsiveLockedInterruptible
void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {mLock.unlock();mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason);  //调用到jni层,向java层传递mLock.lock();
}

ANR 的通知

// Native callback
@SuppressWarnings("unused")
private void notifyWindowUnresponsive(IBinder token, int pid, boolean isPidValid,String reason) {mWindowManagerCallbacks.notifyWindowUnresponsive(token,isPidValid ? OptionalInt.of(pid) : OptionalInt.empty(), reason);
}

调用java层发送弹窗命令, 就不细说了

这篇关于Android14 InputManager-ANR原理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/734788

相关文章

ShardingProxy读写分离之原理、配置与实践过程

《ShardingProxy读写分离之原理、配置与实践过程》ShardingProxy是ApacheShardingSphere的数据库中间件,通过三层架构实现读写分离,解决高并发场景下数据库性能瓶... 目录一、ShardingProxy技术定位与读写分离核心价值1.1 技术定位1.2 读写分离核心价值二

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深入浅出Spring中的@Autowired自动注入的工作原理及实践应用

《深入浅出Spring中的@Autowired自动注入的工作原理及实践应用》在Spring框架的学习旅程中,@Autowired无疑是一个高频出现却又让初学者头疼的注解,它看似简单,却蕴含着Sprin... 目录深入浅出Spring中的@Autowired:自动注入的奥秘什么是依赖注入?@Autowired

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

Python中的filter() 函数的工作原理及应用技巧

《Python中的filter()函数的工作原理及应用技巧》Python的filter()函数用于筛选序列元素,返回迭代器,适合函数式编程,相比列表推导式,内存更优,尤其适用于大数据集,结合lamb... 目录前言一、基本概念基本语法二、使用方式1. 使用 lambda 函数2. 使用普通函数3. 使用 N

MyBatis-Plus 与 Spring Boot 集成原理实战示例

《MyBatis-Plus与SpringBoot集成原理实战示例》MyBatis-Plus通过自动配置与核心组件集成SpringBoot实现零配置,提供分页、逻辑删除等插件化功能,增强MyBa... 目录 一、MyBATis-Plus 简介 二、集成方式(Spring Boot)1. 引入依赖 三、核心机制

redis和redission分布式锁原理及区别说明

《redis和redission分布式锁原理及区别说明》文章对比了synchronized、乐观锁、Redis分布式锁及Redission锁的原理与区别,指出在集群环境下synchronized失效,... 目录Redis和redission分布式锁原理及区别1、有的同伴想到了synchronized关键字

Linux中的HTTPS协议原理分析

《Linux中的HTTPS协议原理分析》文章解释了HTTPS的必要性:HTTP明文传输易被篡改和劫持,HTTPS通过非对称加密协商对称密钥、CA证书认证和混合加密机制,有效防范中间人攻击,保障通信安全... 目录一、什么是加密和解密?二、为什么需要加密?三、常见的加密方式3.1 对称加密3.2非对称加密四、

setsid 命令工作原理和使用案例介绍

《setsid命令工作原理和使用案例介绍》setsid命令在Linux中创建独立会话,使进程脱离终端运行,适用于守护进程和后台任务,通过重定向输出和确保权限,可有效管理长时间运行的进程,本文给大家介... 目录setsid 命令介绍和使用案例基本介绍基本语法主要特点命令参数使用案例1. 在后台运行命令2.

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、