Input dispatching Timedout
Base on: Android 12
Input模块中的InputReader利用EventHub获取数据后生成EventEntry事件,加入到InputDispatcher的消息队列,然后由InputDispatcher负责分发。
InputDispatcher-初始化与开启线程
首先进行InputDispatcher的初始化,然后InputDispatcher::start()开启InputDispatcher线程。
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : mPolicy(policy),
mPendingEvent(nullptr),
mLastDropReason(DropReason::NOT_DROPPED),
mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
mAppSwitchSawKeyDown(false),
mAppSwitchDueTime(LONG_LONG_MAX),
mNextUnblockedEvent(nullptr),
mDispatchEnabled(false),
mDispatchFrozen(false),
mInputFilterEnabled(false),
// mInTouchMode will be initialized by the WindowManager to the default device config.
// To avoid leaking stack in case that call never comes, and for tests,
// initialize it here anyways.
mInTouchMode(true),
mFocusedDisplayId(ADISPLAY_ID_DEFAULT) {
//创建Looper对象
mLooper = new Looper(false);
mReporter = createInputReporter();
mKeyRepeatState.lastKeyEntry = nullptr;
//获取分发超时参数,保存在mConfig
policy->getDispatcherConfiguration(&mConfig);
}
policy是一个NativeInputManager类的智能指针,mConfig是一个InputDispatcherConfiguration类的指针,包含keyRepeatTimeout(默认500ms)和keyRepeatDelay(默认50ms)两个超时参数。
然后开启InputDispatcher线程:
status_t InputDispatcher::start() { if (mThread) {
return ALREADY_EXISTS;
}
mThread = std::make_unique<InputThread>(
"InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
return OK;
}
进入dispatchOnce()代码中。
dispatchOnce()-消息循环
在dispatchOnce()中进行消息循环。通过dispatchOnceInnerLocked()分发事件,每一次消息队列分发完毕后,再用processAnrsLocked()检查是否发生ANR。
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
std::scoped_lock _l(mLock);
//唤醒所有等待线程
mDispatcherIsAlive.notify_all();
// haveCommandsLocked()判断消息队列mCommandQueue是否为空
// 当mCommandQueue不为空时,则派发输入事件
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
// 通过循环处理mCommandQueue中的所有事件.
// 若所有事件执行完毕,将nextWakeupTime设置得极小(即当有新事件到来时立即唤醒线程).
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
// 检查是否发生了ANR,并返回下次执行的时间
const nsecs_t nextAnrCheck = processAnrsLocked();
//选择一个最小的时间,作为下次执行时间
nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
// 若满足条件,进入一个无限长的睡眠,因为没有命令或挂起或排队的事件
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);
}
线程执行Looper->pollOnce,进入epoll_wait等待状态,当发生以下任一情况则退出等待状态:
callback:通过回调方法来唤醒;
timeout:到达nextWakeupTime时间,超时唤醒;
wake: 主动调用Looper的wake()方法;
dispatchOnceInnerLocked()-事件类型分发
根据事件的类型进行分发,又不同的函数处理不同的事件。按键输入使用 dispatchKeyLocked() 进行处理,触屏输入使用dispatchMotionLocked()进行处理。
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
//......
switch (mPendingEvent->type) {
case EventEntry::Type::CONFIGURATION_CHANGED: { //配置改变
//......
break;
}
case EventEntry::Type::DEVICE_RESET: { //设备重置
//......
break;
}
case EventEntry::Type::FOCUS: {
//焦点变化
break;
}
case EventEntry::Type::KEY: { //按键输入
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
//......
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
case EventEntry::Type::MOTION: { //触屏输入
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
//......
done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
}
//......
}
processAnrsLocked()-检查ANR
在processAnrsLocked()中,执行ANR检查,即判断是否出现了ANR,以下两种情况会触发ANR:
- 应用有焦点,但窗口无焦点(即系统在等待出现焦点窗口),且超过ANR等待事件,则进入ANR处理流程;
- 检查第一个connection的状态,若其ANR触发时间到了,则表示发生了ANR。
返回值表示下次检查ANR的时间。
nsecs_t InputDispatcher::processAnrsLocked() {
const nsecs_t currentTime = now();
nsecs_t nextAnrCheck = LONG_LONG_MAX;
// 窗口无焦点,但应用有焦点
if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
if (currentTime >= *mNoFocusedWindowTimeoutTime) { //如果等待时间超过ANR时间
onAnrLocked(mAwaitedFocusedApplication); //执行ANR代码
mAwaitedFocusedApplication.clear();
return LONG_LONG_MIN;
} else { // 未超时,继续等待焦点窗口出现
const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime);
ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining);
nextAnrCheck = *mNoFocusedWindowTimeoutTime;
}
}
// 检查是否有connection中的ANR触发时间到了
nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
if (currentTime < nextAnrCheck) { //还没到检查时间,即还没发生ANR
return nextAnrCheck;
}
// 执行到此处,已经发生了ANR
sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
if (connection == nullptr) {
ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());
return nextAnrCheck;
}
connection->responsive = false; //置connection状态为未响应
// Stop waking up for this unresponsive connection
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
onAnrLocked(connection); //处理ANR
return LONG_LONG_MIN;
}
onANRLocked()-发生ANR
onANRLocked()有两个重载函数,分别对应上述的两种ANR情况,一个是窗口无法得到焦点,另一个是connection状态异常。
// connection的ANR触发时间到产生的ANR
void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
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;
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));
}
// 事件下发,但系统在等待出现焦点窗口
void InputDispatcher::onAnrLocked(const sp<InputApplicationHandle>& application) {
std::string reason = android::base::StringPrintf("%s does not have a focused window",application->getName().c_str());
updateLastAnrStateLocked(application, reason);
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
commandEntry->inputApplicationHandle = application;
commandEntry->inputChannel = nullptr;
commandEntry->reason = std::move(reason);
postCommandLocked(std::move(commandEntry));
}
调用到Framework
调用 onANRLocked() 来捕获 ANR 的相关信息。之后调用到Framework层,大致的调用流程为:
InputDispatcher::onANRLocked() ->
InputDispatcher::doNotifyANRLockedInterruptible() ->
InputDispatcher::notifyANR() ->
InputManagerService.notifyANR() ->
InputMonitor.notifyANR() ->
ActivityManagerService.inputDispatchingTimedOut() ->
AnrHelper.appNotResponding()
最后和其它类型的ANR一样,调用到了AnrHelper.appNotResponding()。
总结
和其它超时不同的是,输入事件的超时是在C++代码InputDispatcher.cpp中进行判断的,在C++中判定超时后通知AMS,AMS再调用appNotResponding处理ANR。
发生Input dispatching Timedout的情况较为复杂,通常是下发到窗口时,窗口出现问题,窗口在Activity执行onResume后才会开始绘制,所以常见的问题有:
- Activity的生命周期耗时:又有很多情况,如生命周期方法中有太多操作、IO耗时、等锁、binder服务端问题等。
- window绘制问题:mDrawState=NO_SURFACE(未绘制)或mDrawState=DRAW_PENDING(正在绘制)。
- 其它。
常见ANR问题
APP的生命周期耗时
APP的onCreate、onStart、onRestart、onStop等方法耗时,通常是APP的问题。
日志示例:
11-03 06:08:33.653 22199 22199 I wm_on_create_called: [0,com.android.settings.MainSettings,performCreate,40582]
说明:onCreate耗时约40s。
11-03 23:15:54.100 3589 3589 I wm_on_start_called: [0,com.android.settings.MainSettings,handleStartActivity,22224]
说明:onStart耗时约22s。
wm_*到wm_*调用延时
看打印wm_*日志的所在线程是否有等锁。
# AppReviewsActivity执行onDestroy过程中,wm_destroy_activity打印慢
05-22 03:51:29.734 1813 6406 I wm_add_to_stopping: [0,133348335,com.xiaomi.market/.h52native.detail.AppReviewsActivity,completeFinishing]
05-22 03:51:38.767 1813 1916 I wm_destroy_activity: [0,133348335,3596,com.xiaomi.market/.h52native.detail.AppReviewsActivity,finish-imm:idle]
# 可以在日志中查看wm_destroy_activity日志所在线程1916等哪个锁
05-22 03:51:36.662 1813 1916 I dvm_lock_sample: [system_server,1,android.display,6920,WindowManagerService.java,5371,void com.android.server.wm.WindowManagerService.reportFocusChanged(android.os.IBinder, android.os.IBinder),ActivityTaskManagerService.java,7055,boolean com.android.server.wm.ActivityTaskManagerService.isInSplitScreenWindowingMode(),6406]
wm_*_*到wm_on_*_called调用延时
(1) 是Slow Binder或等锁(dvm_lock_sample日志)导致的延时。
(2) APP的生命周期耗时
wm_on_*_called日志的打印要APP的生命周期方法onXXXX完成后才打印,若APP的生命周期耗时,则打印时间也有延迟。
APP无wm_on_xxx调用
日志中只有system_server端wm_xxx_activity的调用信息,但没有wm_on_xxx_called,APP端没有调用Activity的生命周期方法。
(1) Binder问题
可能是binder方面的问题,通知Activity启动的消息未发送到应用。如APP端Binder线程池已满。
(2) 等锁导致
system端代码等锁时间太长,以至于无法调用到APP端。
生命周期正常,窗口绘制问题
Activity的生命周期正常,但window绘制较慢,导致焦点无法进入。
若一段时间后焦点仍无法进入窗口,则会发生ANR(Input dispatching timed out)。
设置的时间到达时窗口仍没有绘制完成。通常为WMS的问题。若有dumpsys log,可以通过关键字”WindowStateAnimator{.*Activity名}”查看,如:
WindowStateAnimator{19ca2f0 com.android.email/com.kingsoft.email2.ui.MailActivityEmail}:
mDrawState=NO_SURFACE mLastHidden=true
mEnterAnimationPending=false mSystemDecorRect=[0,0][919,1653] mLastClipRect=[0,0][919,1653]
mGlobalScale=0.70185184 mDsDx=0.70185184 mDtDx=0.0 mDtDy=0.0 mDsDy=0.70185184
mDrawState=NO_SURFACE表示还没有绘制。
QueuedWork.processPendingWork()
使用SharedPreferences修改配置文件后,调用apply()提交。apply()首先写入内存,然后将写入到磁盘(落盘)的任务加入到队列中,通过异步线程做落盘的操作。
QueuedWork工作在queued-work-looper线程中,主要是用来执行和跟踪一些进程全局的工作,就是由它调度的SP相关的异步任务,通过调用调用QueuedWork.queue()将SP文件变更操作的任务发送到QueuedWork类中执行。
而APP会在handlePauseActivity()和handleStopActivity()方法中(在onPause和onStop执行之后)调用QueuedWork.waitToFinish(),保证这些SP任务都已经完成,如果写入比较慢,主线程就会卡顿,甚至ANR。常见于以下情况:
- 将较大的String写入到SP中;
- 系统资源较为紧张,CPU或内存占用大;
- 在monkey压测场景中,IO比较频繁。
SharedPreferences.Editor editor = getSharedPreferences("fileName",MODEPRIVATE).edit();
editor.putString("Key", "Value");
editor.apply();
发生ANR的堆栈:
......
at android.app.SharedPreferencesImpl.writeToFile(SharedPreferencesImpl.java:738)
at android.app.SharedPreferencesImpl.access$900(SharedPreferencesImpl.java:59)
at android.app.SharedPreferencesImpl$2.run(SharedPreferencesImpl.java:672)
locked <0x0ebcf9c3> (a java.lang.Object)
at android.app.QueuedWork.processPendingWork(QueuedWork.java:299)
locked <0x0a61f240> (a java.lang.Object)
at android.app.QueuedWork.waitToFinish(QueuedWork.java:190)
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:5014)
at android.app.servertransaction.StopActivityItem.execute(StopActivityItem.java:40)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2116)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:236)
at android.app.ActivityThread.main(ActivityThread.java:7904)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
无Focus entering
Focus request后没有Focus enter,导致ANR。日志中显示原因为:Input dispatching timed out (Application does not have a focused window)。