基于Android 6.0源码, 分析Input事件发生ANR的原理
一. ANR概述
当input事件处理得慢就会触发ANR,那ANR内部原理是什么,哪些场景会产生ANR呢。 “工欲善其事必先利其器”,为了理解input ANR原理,前面几篇文章疏通了整个input框架的处理流程,都是为了这篇文章而做铺垫。在正式开始分析ANR触发原理以及触发场景之前,先来回顾一下input流程。
- Input系统—启动篇:Input系统也是伴随着system_server进程启动过程而启动。
- Input系统—InputReader线程:通过EventHub从/dev/input节点获取事件,转换成EventEntry事件加入到InputDispatcher的mInboundQueue。
- Input系统—InputDispatcher线程:从mInboundQueue队列取出事件,转换成DispatchEntry事件加入到connection的outboundQueue队列。再然后开始处理分发事件,取出outbound队列,放入waitQueue.
- Input系统—UI线程:Activity的onResume执行完便会向WMS注册窗口信息,并建立socket pair用于跨进程通信;
- Input系统—进程交互:系统进程的”InputDispatcher”线程和应用进程的UI主线程通过socket pair进行交互;
有了以上基础,再来从ANR视角看看Input系统。
1.1 InputReader
InputReader的主要工作分两部分:
- 调用EventHub的getEvents()读取节点/dev/input的input_event结构体转换成RawEvent结构体,RawEvent根据不同InputMapper来转换成相应的EventEntry,比如按键事件则对应KeyEntry,触摸事件则对应MotionEntry。
- 转换结果:inut_event -> EventEntry;
- 将事件添加到mInboundQueue队列尾部,加入该队列前有以下两个过滤:
- IMS.interceptKeyBeforeQueueing:事件分发前可增加业务逻辑;
- IMS.filterInputEvent:可拦截事件,当返回值为false的事件都直接拦截,没有机会加入mInboundQueue队列,不会再往下分发;否则进入下一步;
- enqueueInboundEventLocked:该事件放入mInboundQueue队列尾部;
- mLooper->wake:并根据情况来唤醒InputDispatcher线程.
1.2 InputDispatcher
- dispatchOnceInnerLocked(): 从InputDispatcher的
mInboundQueue
队列,取出事件EventEntry。另外该方法开始执行的时间点(currentTime)便是后续事件dispatchEntry的分发时间(deliveryTime) - dispatchKeyLocked():满足一定条件时会添加命令doInterceptKeyBeforeDispatchingLockedInterruptible;
- enqueueDispatchEntryLocked():生成事件DispatchEntry并加入connection的
outbound
队列 - startDispatchCycleLocked():从outboundQueue中取出事件DispatchEntry, 重新放入connection的
waitQueue
队列; - runCommandsLockedInterruptible():通过循环遍历地方式,依次处理mCommandQueue队列中的所有命令。而mCommandQueue队列中的命令是通过postCommandLocked()方式向该队列添加的。ANR回调命令便是在这个时机执行。
- handleTargetsNotReadyLocked(): 该过程会判断是否等待超过5s来决定是否调用onANRLocked().
1.3 UI线程
- “InputDispatcher”线程监听socket服务端,收到消息后回调InputDispatcher.handleReceiveCallback();
- UI主线程监听socket客户端,收到消息后回调NativeInputEventReceiver.handleEvent().
对于ANR的触发主要是在InputDispatcher过程,下面再从ANR的角度来说一说ANR触发过程。
二. ANR触发机制
2.1 dispatchOnceInnerLocked
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now(); //当前时间
if (!mDispatchEnabled) { //默认值为false
resetKeyRepeatLocked(); //重置操作
}
if (mDispatchFrozen) { //默认值为false
return; //当分发被冻结,则不再处理超时和分发事件的工作,直接返回
}
//优化app切换延迟,当切换超时,则抢占分发,丢弃其他所有即将要处理的事件。
bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
...
if (!mPendingEvent) {
if (mInboundQueue.isEmpty()) {
if (!mPendingEvent) {
return; //没有事件需要处理,则直接返回
}
} else {
//从mInboundQueue取出头部的事件
mPendingEvent = mInboundQueue.dequeueAtHead();
}
...
resetANRTimeoutsLocked(); //重置ANR信息[见小节2.1.1]
}
bool done = false;
DropReason dropReason = DROP_REASON_NOT_DROPPED;
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
dropReason = DROP_REASON_POLICY;
} else if (!mDispatchEnabled) {
dropReason = DROP_REASON_DISABLED;
}
...
switch (mPendingEvent->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
if (isAppSwitchDue) {
if (isAppSwitchKeyEventLocked(typedEntry)) {
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DROP_REASON_NOT_DROPPED) {
dropReason = DROP_REASON_APP_SWITCH;
}
}
if (dropReason == DROP_REASON_NOT_DROPPED
&& isStaleEventLocked(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DROP_REASON_BLOCKED;
}
// 分发按键事件[见小节2.2]
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
...
}
...
//分发操作完成,则进入该分支
if (done) {
if (dropReason != DROP_REASON_NOT_DROPPED) {
//[见小节2.1.2]
dropInboundEventLocked(mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
releasePendingEventLocked(); //释放pending事件
*nextWakeupTime = LONG_LONG_MIN; //强制立刻执行轮询
}
}
在enqueueInboundEventLocked()的过程中已设置mAppSwitchDueTime等于eventTime加上500ms:
mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
该方法主要功能:
- mDispatchFrozen用于决定是否冻结事件分发工作不再往下执行;
- 当事件分发的时间点距离该事件加入mInboundQueue的时间超过500ms,则认为app切换过期,即isAppSwitchDue=true;
- mInboundQueue不为空,则取出头部的事件,放入mPendingEvent变量;并重置ANR时间;
- 根据情况设置dropReason;
- 根据EventEntry的type类型分别处理,比如按键调用dispatchKeyLocked分发事件;
- 执行完成后,根据dropReason来决定是否丢失事件,以及释放当前事件;
接下来以按键为例来展开说明, 则进入[小节2.2] dispatchKeyLocked
2.1.1 resetANRTimeoutsLocked
void InputDispatcher::resetANRTimeoutsLocked() {
// 重置等待超时cause和handle
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
mInputTargetWaitApplicationHandle.clear();
}
2.1.2 dropInboundEventLocked
void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
const char* reason;
switch (dropReason) {
case DROP_REASON_POLICY:
reason = "inbound event was dropped because the policy consumed it";
break;
case DROP_REASON_DISABLED:
if (mLastDropReason != DROP_REASON_DISABLED) {
ALOGI("Dropped event because input dispatch is disabled.");
}
reason = "inbound event was dropped because input dispatch is disabled";
break;
case DROP_REASON_APP_SWITCH:
ALOGI("Dropped event because of pending overdue app switch.");
reason = "inbound event was dropped because of pending overdue app switch";
break;
case DROP_REASON_BLOCKED:
ALOGI("Dropped event because the current application is not responding and the user ""has started interacting with a different application.");
reason = "inbound event was dropped because the current application is not responding ""and the user has started interacting with a different application";
break;
case DROP_REASON_STALE:
ALOGI("Dropped event because it is stale.");
reason = "inbound event was dropped because it is stale";
break;
default:
return;
}
switch (entry->type) {
case EventEntry::TYPE_KEY: {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
break;
}
...
}
}
2.2 dispatchKeyLocked
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
...
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
//当前时间小于唤醒时间,则进入等待状态。
if (currentTime < entry->interceptKeyWakeupTime) {
if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
*nextWakeupTime = entry->interceptKeyWakeupTime;
}
return false; //直接返回
}
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
entry->interceptKeyWakeupTime = 0;
}
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
//让policy有机会执行拦截操作
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
if (mFocusedWindowHandle != NULL) {
commandEntry->inputWindowHandle = mFocusedWindowHandle;
}
commandEntry->keyEntry = entry;
entry->refCount += 1;
return false; //直接返回
} else {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
}
} else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
if (*dropReason == DROP_REASON_NOT_DROPPED) {
*dropReason = DROP_REASON_POLICY;
}
}
//如果需要丢弃该事件,则执行清理操作
if (*dropReason != DROP_REASON_NOT_DROPPED) {
setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
return true; //直接返回
}
Vector<InputTarget> inputTargets;
// 【见小节2.3】
int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime);
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false; //直接返回
}
setInjectionResultLocked(entry, injectionResult);
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
return true; //直接返回
}
addMonitoringTargetsLocked(inputTargets);
//只有injectionResult是成功,才有机会执行分发事件【见小节2.5】
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
在以下场景下,有可能无法分发事件:
- 当前时间小于唤醒时间(nextWakeupTime)的情况;
- policy需要提前拦截事件的情况;
- 需要drop事件的情况;
- 寻找聚焦窗口失败的情况;
如果成功跳过以上所有情况,则会进入执行事件分发的过程。
2.3 findFocusedWindowTargetsLocked
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
int32_t injectionResult;
String8 reason;
if (mFocusedWindowHandle == NULL) {
if (mFocusedApplicationHandle != NULL) {
//【见小节2.3.2】
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplicationHandle, NULL, nextWakeupTime,
"Waiting because no window has focus but there is a ""focused application that may eventually add a window ""when it finishes starting up.");
goto Unresponsive;
}
ALOGI("Dropping event because there is no focused window or focused application.");
injectionResult = INPUT_EVENT_INJECTION_FAILED;
goto Failed;
}
//权限检查
if (! checkInjectionPermission(mFocusedWindowHandle, entry->injectionState)) {
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
goto Failed;
}
//检测窗口是否为更多的输入操作而准备就绪【见小节2.3.1】
reason = checkWindowReadyForMoreInputLocked(currentTime,
mFocusedWindowHandle, entry, "focused");
if (!reason.isEmpty()) {
//【见小节2.3.2】
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, reason.string());
goto Unresponsive;
}
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
//成功找到目标窗口,添加到目标窗口
addWindowTargetLocked(mFocusedWindowHandle,
InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
inputTargets);
Failed:
Unresponsive:
//TODO: 统计等待时长信息,目前没有实现,这个方法还是很值得去改造
nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
updateDispatchStatisticsLocked(currentTime, entry,
injectionResult, timeSpentWaitingForApplication);
return injectionResult;
}
寻找聚焦窗口失败的情况:
- 无窗口,无应用:Dropping event because there is no focused window or focused application.(这并不导致ANR的情况,因为没有机会调用handleTargetsNotReadyLocked)
- 无窗口, 有应用:Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.
另外,还有更多多的失败场景见checkWindowReadyForMoreInputLocked的过程,如下:
2.3.1 checkWindowReadyForMoreInputLocked
String8 InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
const char* targetType) {
//当窗口暂停的情况,则保持等待
if (windowHandle->getInfo()->paused) {
return String8::format("Waiting because the %s window is paused.", targetType);
}
//当窗口连接未注册,则保持等待
ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
if (connectionIndex < 0) {
return String8::format("Waiting because the %s window's input channel is not ""registered with the input dispatcher. The window may be in the process ""of being removed.", targetType);
}
//当窗口连接已死亡,则保持等待
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
if (connection->status != Connection::STATUS_NORMAL) {
return String8::format("Waiting because the %s window's input connection is %s.""The window may be in the process of being removed.", targetType,
connection->getStatusLabel());
}
// 当窗口连接已满,则保持等待
if (connection->inputPublisherBlocked) {
return String8::format("Waiting because the %s window's input channel is full. ""Outbound queue length: %d. Wait queue length: %d.",
targetType, connection->outboundQueue.count(), connection->waitQueue.count());
}
//确保分发队列,并没有存储过多事件
if (eventEntry->type == EventEntry::TYPE_KEY) {
if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty()) {
return String8::format("Waiting to send key event because the %s window has not ""finished processing all of the input events that were previously ""delivered to it. Outbound queue length: %d. Wait queue length: %d.",
targetType, connection->outboundQueue.count(), connection->waitQueue.count());
}
} else {
if (!connection->waitQueue.isEmpty()
&& currentTime >= connection->waitQueue.head->deliveryTime
+ STREAM_AHEAD_EVENT_TIMEOUT) {
return String8::format("Waiting to send non-key event because the %s window has not ""finished processing certain input events that were delivered to it over ""%0.1fms ago. Wait queue length: %d. Wait queue head age: %0.1fms.",
targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
connection->waitQueue.count(),
(currentTime - connection->waitQueue.head->deliveryTime) * 0.000001f);
}
}
return String8::empty();
}
2.3.2 handleTargetsNotReadyLocked
int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
const EventEntry* entry,
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
nsecs_t* nextWakeupTime, const char* reason) {
if (applicationHandle == NULL && windowHandle == NULL) {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
mInputTargetWaitStartTime = currentTime; //当前时间
mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
mInputTargetWaitTimeoutExpired = false;
mInputTargetWaitApplicationHandle.clear();
}
} else {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
nsecs_t timeout;
if (windowHandle != NULL) {
timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else if (applicationHandle != NULL) {
timeout = applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
} else {
timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT; // 5s
}
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
mInputTargetWaitStartTime = currentTime; //当前时间
mInputTargetWaitTimeoutTime = currentTime + timeout;
mInputTargetWaitTimeoutExpired = false;
mInputTargetWaitApplicationHandle.clear();
if (windowHandle != NULL) {
mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle;
}
if (mInputTargetWaitApplicationHandle == NULL && applicationHandle != NULL) {
mInputTargetWaitApplicationHandle = applicationHandle;
}
}
}
if (mInputTargetWaitTimeoutExpired) {
return INPUT_EVENT_INJECTION_TIMED_OUT; //等待超时已过期,则直接返回
}
//当超时5s则进入ANR流程
if (currentTime >= mInputTargetWaitTimeoutTime) {
onANRLocked(currentTime, applicationHandle, windowHandle,
entry->eventTime, mInputTargetWaitStartTime, reason);
*nextWakeupTime = LONG_LONG_MIN; //强制立刻执行轮询来执行ANR策略
return INPUT_EVENT_INJECTION_PENDING;
} else {
if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
*nextWakeupTime = mInputTargetWaitTimeoutTime; //当触发超时则强制执行轮询
}
return INPUT_EVENT_INJECTION_PENDING;
}
}
- 当applicationHandle和windowHandle同时为空, 且system准备就绪的情况下
- 设置等待理由 INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
- 设置超时等待时长为无限大;
- 设置TimeoutExpired= false
- 清空等待队列;
- 当applicationHandle和windowHandle至少一个不为空, 且application准备就绪的情况下:
- 设置等待理由 INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
- 设置超时等待时长为5s;
- 设置TimeoutExpired= false
- 清空等待队列;
继续回到[小节2.3]findFocusedWindowTargetsLocked,如果没有发生ANR,则addWindowTargetLocked()将该事件添加到inputTargets。
2.4 dispatchEventLocked
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
//向mCommandQueue队列添加doPokeUserActivityLockedInterruptible命令
pokeUserActivityLocked(eventEntry);
for (size_t i = 0; i < inputTargets.size(); i++) {
const InputTarget& inputTarget = inputTargets.itemAt(i);
ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
//找到目标连接[见小节2.5]
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
}
}
}
该方法主要功能是将eventEntry发送到目标inputTargets.
2.5 prepareDispatchCycleLocked
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
if (connection->status != Connection::STATUS_NORMAL) {
return; //当连接已破坏,则直接返回
}
...
//[见小节2.6]
enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}
2.6 enqueueDispatchEntriesLocked
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
bool wasEmpty = connection->outboundQueue.isEmpty();
//[见小节2.7]
enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
...
if (wasEmpty && !connection->outboundQueue.isEmpty()) {
//当原先的outbound队列为空, 且当前outbound不为空的情况执行.[见小节2.8]
startDispatchCycleLocked(currentTime, connection);
}
}
2.7 enqueueDispatchEntryLocked
void InputDispatcher::enqueueDispatchEntryLocked(
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_t dispatchMode) {
int32_t inputTargetFlags = inputTarget->flags;
if (!(inputTargetFlags & dispatchMode)) {
return; //分发模式不匹配,则直接返回
}
inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;
//生成新的事件, 加入connection的outbound队列
DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry,
inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
inputTarget->scaleFactor);
...
//添加到outboundQueue队尾
connection->outboundQueue.enqueueAtTail(dispatchEntry);
}
该方法主要功能:
- 根据dispatchMode来决定是否需要加入outboundQueue队列;
- 根据EventEntry,来生成DispatchEntry事件;
- 将dispatchEntry加入到connection的outbound队列.
执行到这里,其实等于由做了一次搬运的工作,将InputDispatcher中mInboundQueue中的事件取出后, 找到目标window后,封装dispatchEntry加入到connection的outbound队列.
如果当connection原先的outbound队列为空, 经过enqueueDispatchEntryLocked处理后, 该outbound不为空的情况下, 则执行startDispatchCycleLocked()方法.
2.8 startDispatchCycleLocked
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection) {
//当Connection状态正常,且outboundQueue不为空
while (connection->status == Connection::STATUS_NORMAL
&& !connection->outboundQueue.isEmpty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.head;
dispatchEntry->deliveryTime = currentTime; //设置deliveryTime时间
status_t status;
EventEntry* eventEntry = dispatchEntry->eventEntry;
switch (eventEntry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
//发布按键时间 [见小节2.9]
status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
keyEntry->deviceId, keyEntry->source,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
keyEntry->keyCode, keyEntry->scanCode,
keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
keyEntry->eventTime);
break;
}
...
}
//发布结果分析
if (status) {
if (status == WOULD_BLOCK) {
if (connection->waitQueue.isEmpty()) {
//pipe已满,但waitQueue为空. 不正常的行为
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
} else {
// 处于阻塞状态
connection->inputPublisherBlocked = true;
}
} else {
//不不正常的行为
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
}
return;
}
//从outboundQueue中取出事件,重新放入waitQueue队列
connection->outboundQueue.dequeue(dispatchEntry);
connection->waitQueue.enqueueAtTail(dispatchEntry);
}
}
startDispatchCycleLocked的主要功能: 从outboundQueue中取出事件,重新放入waitQueue队列
- abortBrokenDispatchCycleLocked()方法最终会调用到Java层的IMS.notifyInputChannelBroken().
2.9 inputPublisher.publishKeyEvent
[-> InputTransport.cpp]
status_t InputPublisher::publishKeyEvent(...) {
if (!seq) {
return BAD_VALUE;
}
InputMessage msg;
msg.header.type = InputMessage::TYPE_KEY;
msg.body.key.seq = seq;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
msg.body.key.action = action;
msg.body.key.flags = flags;
msg.body.key.keyCode = keyCode;
msg.body.key.scanCode = scanCode;
msg.body.key.metaState = metaState;
msg.body.key.repeatCount = repeatCount;
msg.body.key.downTime = downTime;
msg.body.key.eventTime = eventTime;
return mChannel->sendMessage(&msg);
}
2.10 下一步
经过Input系统—进程交互几经周折,runCommandsLockedInterruptible过程处理的便是如下命令:
void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(
CommandEntry* commandEntry) {
sp<Connection> connection = commandEntry->connection;
nsecs_t finishTime = commandEntry->eventTime;
uint32_t seq = commandEntry->seq;
bool handled = commandEntry->handled;
//获取分发事件
DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);
if (dispatchEntry) {
nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
//打印出所有分发时间超过2s的事件
if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
String8 msg;
msg.appendFormat("Window '%s' spent %0.1fms processing the last input event: ",
connection->getWindowName(), eventDuration * 0.000001f);
dispatchEntry->eventEntry->appendDescription(msg);
ALOGI("%s", msg.string());
}
bool restartEvent;
if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
restartEvent = afterKeyEventLockedInterruptible(connection,
dispatchEntry, keyEntry, handled);
}
...
if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
//将dispatchEntry事件从等待队列(waitQueue)中移除
connection->waitQueue.dequeue(dispatchEntry);
if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
connection->outboundQueue.enqueueAtHead(dispatchEntry);
} else {
releaseDispatchEntryLocked(dispatchEntry);
}
}
//启动下一个事件处理循环。
startDispatchCycleLocked(now(), connection);
}
}
只有收到该命令,才完成dispatchEntry事件从等待队列(waitQueue
)中移除操作。 这便是完整的一次input事件处理过程。
三. command
当执行findFocusedWindowTargetsLocked()过程调用到handleTargetsNotReadyLocked,且满足超时5s的情况则会调用onANRLocked().
3.1 onANRLocked
[-> InputDispatcher.cpp]
void InputDispatcher::onANRLocked(
nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) {
float dispatchLatency = (currentTime - eventTime) * 0.000001f;
float waitDuration = (currentTime - waitStartTime) * 0.000001f;
ALOGI("Application is not responding: %s. ""It has been %0.1fms since event, %0.1fms since wait started. Reason: %s",
getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(),
dispatchLatency, waitDuration, reason);
//捕获ANR的现场信息
time_t t = time(NULL);
struct tm tm;
localtime_r(&t, &tm);
char timestr[64];
strftime(timestr, sizeof(timestr), "%F %T", &tm);
mLastANRState.clear();
mLastANRState.append(INDENT "ANR:\n");
mLastANRState.appendFormat(INDENT2 "Time: %s\n", timestr);
mLastANRState.appendFormat(INDENT2 "Window: %s\n",
getApplicationWindowLabelLocked(applicationHandle, windowHandle).string());
mLastANRState.appendFormat(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
mLastANRState.appendFormat(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
mLastANRState.appendFormat(INDENT2 "Reason: %s\n", reason);
dumpDispatchStateLocked(mLastANRState);
//将ANR命令加入mCommandQueue
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doNotifyANRLockedInterruptible);
commandEntry->inputApplicationHandle = applicationHandle;
commandEntry->inputWindowHandle = windowHandle;
commandEntry->reason = reason;
}
发生ANR调用onANRLocked()的过程会将doNotifyANRLockedInterruptible加入mCommandQueue。 在下一轮InputDispatcher.dispatchOnce的过程中会先执行runCommandsLockedInterruptible()方法,取出 mCommandQueue队列的所有命令逐一执行。那么ANR所对应的命令doNotifyANRLockedInterruptible,接下来看该方法。
3.2 doNotifyANRLockedInterruptible
[-> InputDispatcher.cpp]
void InputDispatcher::doNotifyANRLockedInterruptible(
CommandEntry* commandEntry) {
mLock.unlock();
//[见小节3.3]
nsecs_t newTimeout = mPolicy->notifyANR(
commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle,
commandEntry->reason);
mLock.lock();
//newTimeout =5s [见小节3.8]
resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
commandEntry->inputWindowHandle != NULL
? commandEntry->inputWindowHandle->getInputChannel() : NULL);
}
mPolicy是指NativeInputManager
3.3 NativeInputManager.notifyANR
[-> com_android_server_input_InputManagerService.cpp]
nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
const sp<InputWindowHandle>& inputWindowHandle, const String8& reason) {
JNIEnv* env = jniEnv();
jobject inputApplicationHandleObj =
getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);
jobject inputWindowHandleObj =
getInputWindowHandleObjLocalRef(env, inputWindowHandle);
jstring reasonObj = env->NewStringUTF(reason.string());
//调用Java方法[见小节3.4]
jlong newTimeout = env->CallLongMethod(mServiceObj,
gServiceClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj,
reasonObj);
if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
newTimeout = 0; //抛出异常,则清理并重置timeout
}
...
return newTimeout;
}
先看看register_android_server_InputManager过程:
int register_android_server_InputManager(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "com/android/server/input/InputManagerService",
gInputManagerMethods, NELEM(gInputManagerMethods));
jclass clazz;
FIND_CLASS(clazz, "com/android/server/input/InputManagerService");
...
GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz,
"notifyANR",
"(Lcom/android/server/input/InputApplicationHandle;Lcom/android/server/input/InputWindowHandle;Ljava/lang/String;)J");
...
}
可知gServiceClassInfo.notifyANR是指IMS.notifyANR
3.4 IMS.notifyANR
[-> InputManagerService.java]
private long notifyANR(InputApplicationHandle inputApplicationHandle,
InputWindowHandle inputWindowHandle, String reason) {
//[见小节3.5]
return mWindowManagerCallbacks.notifyANR(
inputApplicationHandle, inputWindowHandle, reason);
}
此处mWindowManagerCallbacks是指InputMonitor对象。
3.5 InputMonitor.notifyANR
[-> InputMonitor.java]
public long notifyANR(InputApplicationHandle inputApplicationHandle,
InputWindowHandle inputWindowHandle, String reason) {
AppWindowToken appWindowToken = null;
WindowState windowState = null;
boolean aboveSystem = false;
synchronized (mService.mWindowMap) {
if (inputWindowHandle != null) {
windowState = (WindowState) inputWindowHandle.windowState;
if (windowState != null) {
appWindowToken = windowState.mAppToken;
}
}
if (appWindowToken == null && inputApplicationHandle != null) {
appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;
}
//输出input事件分发超时log
if (windowState != null) {
Slog.i(WindowManagerService.TAG, "Input event dispatching timed out "
+ "sending to " + windowState.mAttrs.getTitle()
+ ". Reason: " + reason);
int systemAlertLayer = mService.mPolicy.windowTypeToLayerLw(
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
aboveSystem = windowState.mBaseLayer > systemAlertLayer;
} else if (appWindowToken != null) {
Slog.i(WindowManagerService.TAG, "Input event dispatching timed out "
+ "sending to application " + appWindowToken.stringName
+ ". Reason: " + reason);
} else {
Slog.i(WindowManagerService.TAG, "Input event dispatching timed out "
+ ". Reason: " + reason);
}
mService.saveANRStateLocked(appWindowToken, windowState, reason);
}
if (appWindowToken != null && appWindowToken.appToken != null) {
//【见小节3.6.1】
boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(reason);
if (! abort) {
return appWindowToken.inputDispatchingTimeoutNanos; //5s
}
} else if (windowState != null) {
//【见小节3.6.2】
long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut(
windowState.mSession.mPid, aboveSystem, reason);
if (timeout >= 0) {
return timeout * 1000000L; //5s
}
}
return 0;
}
发生input相关的ANR时在system log输出ANR信息。
3.6 DispatchingTimedOut
3.6.1 Token.keyDispatchingTimedOut
[-> ActivityRecord.java :: Token]
final class ActivityRecord {
static class Token extends IApplicationToken.Stub {
public boolean keyDispatchingTimedOut(String reason) {
ActivityRecord r;
ActivityRecord anrActivity;
ProcessRecord anrApp;
synchronized (mService) {
r = tokenToActivityRecordLocked(this);
if (r == null) {
return false;
}
anrActivity = r.getWaitingHistoryRecordLocked();
anrApp = r != null ? r.app : null;
}
//[见小节3.7]
return mService.inputDispatchingTimedOut(anrApp, anrActivity, r, false, reason);
}
...
}
}
3.6.2 AMS.inputDispatchingTimedOut
public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
...
ProcessRecord proc;
long timeout;
synchronized (this) {
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pid); //根据pid查看进程record
}
timeout = getInputDispatchingTimeoutLocked(proc);
}
//【见小节3.7】
if (!inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
return -1;
}
return timeout;
}
inputDispatching的超时为KEY_DISPATCHING_TIMEOUT
,即timeout = 5s。
3.7 AMS.inputDispatchingTimedOut
public boolean inputDispatchingTimedOut(final ProcessRecord proc,
final ActivityRecord activity, final ActivityRecord parent,
final boolean aboveSystem, String reason) {
...
final String annotation;
if (reason == null) {
annotation = "Input dispatching timed out";
} else {
annotation = "Input dispatching timed out (" + reason + ")";
}
if (proc != null) {
...
//通过handler机制,交由“ActivityManager”线程执行ANR处理过程。
mHandler.post(new Runnable() {
public void run() {
appNotResponding(proc, activity, parent, aboveSystem, annotation);
}
});
}
return true;
}
appNotResponding会输出现场的重要进程的trace等信息。 再回到【小节3.2】处理完ANR后再调用resumeAfterTargetsNotReadyTimeoutLocked。
3.8 resumeAfterTargetsNotReadyTimeoutLocked
[-> InputDispatcher.cpp]
void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
const sp<InputChannel>& inputChannel) {
if (newTimeout > 0) {
//超时时间增加5s
mInputTargetWaitTimeoutTime = now() + newTimeout;
} else {
// Give up.
mInputTargetWaitTimeoutExpired = true;
// Input state will not be realistic. Mark it out of sync.
if (inputChannel.get()) {
ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
sp<InputWindowHandle> windowHandle = connection->inputWindowHandle;
if (windowHandle != NULL) {
const InputWindowInfo* info = windowHandle->getInfo();
if (info) {
ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(info->displayId);
if (stateIndex >= 0) {
mTouchStatesByDisplay.editValueAt(stateIndex).removeWindow(
windowHandle);
}
}
}
if (connection->status == Connection::STATUS_NORMAL) {
CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
"application not responding");
synthesizeCancelationEventsForConnectionLocked(connection, options);
}
}
}
}
}
四. input死锁监测机制
[-> InputManagerService.java]
public void start() {
...
Watchdog.getInstance().addMonitor(this);
...
}
InputManagerService实现了Watchdog.Monitor接口, 并且在启动过程将自己加入到了Watchdog线程的monitor队列.
4.1 IMS.monitor
Watchdog便会定时调用IMS.monitor()方法.
public void monitor() {
synchronized (mInputFilterLock) { }
nativeMonitor(mPtr);
}
nativeMonitor经过JNI调用,进如如下方法
static void nativeMonitor(JNIEnv* /* env /, jclass / clazz /, jlong ptr) { NativeInputManager im = reinterpret_cast<NativeInputManager*>(ptr);
im->getInputManager()->getReader()->monitor(); //见小节4.2
im->getInputManager()->getDispatcher()->monitor(); //见小节4.4 }
4.2 InputReader
[-> InputReader.cpp]
void InputReader::monitor() {
//请求和释放一次mLock,来确保reader没有发生死锁的问题
mLock.lock();
mEventHub->wake();
mReaderIsAliveCondition.wait(mLock);
mLock.unlock();
//监测EventHub
mEventHub->monitor();
}
获取mLock之后进入Condition类型的wait()方法,等待InputReader线程的loopOnce()中的broadcast()来唤醒.
void InputReader::loopOnce() {
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
...
{
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) {
processEventsLocked(mEventBuffer, count);
}
}
...
mQueuedListener->flush();
}
4.3 EventHub
[-> EventHub.cpp]
void EventHub::monitor() {
//请求和释放一次mLock,来确保reader没有发生死锁的问题
mLock.lock();
mLock.unlock();
}
4.4 InputDispatcher
[-> InputDispatcher.cpp]
void InputDispatcher::monitor() {
mLock.lock();
mLooper->wake();
mDispatcherIsAliveCondition.wait(mLock);
mLock.unlock();
}
获取mLock之后进入Condition类型的wait()方法,等待IInputDispatcher线程的loopOnce()中的broadcast()来唤醒.
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{
AutoMutex _l(mLock);
mDispatcherIsAliveCondition.broadcast();
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
}
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis); //进入epoll_wait
}
五. 总结
5.1 dispatching超时类型
由小节[3.5] InputMonitor.notifyANR完成, 当发生ANR时system log中会出现以下信息:
Input event dispatching timed out xxx. Reason: + reason
, 其中xxx取值:
- 窗口类型: sending to
windowState.mAttrs.getTitle()
- 应用类型: sending to application
appWindowToken.stringName
- 其他类型: 则为空.
5.2 reason类型
由小节[2.3.1]checkWindowReadyForMoreInputLocked完成, ANR reason主要有以下几类:
- 无窗口, 有应用:Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.
- 窗口连接已死亡:Waiting because the [targetType] window’s input connection is [Connection.Status]. The window may be in the process of being removed.
- 窗口连接已满:Waiting because the [targetType] window’s input channel is full. Outbound queue length: [outboundQueue长度]. Wait queue length: [waitQueue长度].
- 按键事件,输出队列或事件等待队列不为空:Waiting to send key event because the [targetType] window has not finished processing all of the input events that were previously delivered to it. Outbound queue length: [outboundQueue长度]. Wait queue length: [waitQueue长度].
- 非按键事件,事件等待队列不为空且头事件分发超时500ms:Waiting to send non-key event because the [targetType] window has not finished processing certain input events that were delivered to it over 500ms ago. Wait queue length: [waitQueue长度]. Wait queue head age: [等待时长].
其中
- targetType的取值为”focused”或者”touched”
- Connection.Status的取值为”NORMAL”,”BROKEN”,”ZOMBIE”
5.3 dropReason类型
由小节[2.1.2] dropInboundEventLocked完成,输出事件丢弃的原因:
- DROP_REASON_POLICY: “inbound event was dropped because the policy consumed it”;
- DROP_REASON_DISABLED: “inbound event was dropped because input dispatch is disabled”;
- DROP_REASON_APP_SWITCH: “inbound event was dropped because of pending overdue app switch”;
- DROP_REASON_BLOCKED: “inbound event was dropped because the current application is not responding and the user has started interacting with a different application””;
- DROP_REASON_STALE: “inbound event was dropped because it is stale”;
其他:
- doDispatchCycleFinishedLockedInterruptible的过程, 会记录分发时间超过2s的事件,
- findFocusedWindowTargetsLocked的过程, 可以统计等待时长信息过程测信息.
5.4 input死锁监测机制
通过将InputManagerService加入到Watchdog的monitor队列,定时监测是否发生死锁. 整个监测过涉及EventHub, InputReader, InputDispatcher, InputManagerService的死锁监测. 监测的原理很简单,通过尝试获取锁并释放锁的方式.
最后, 可通过adb shell dumpsys input
来查看手机当前的input状态, 输出内容分别为EventHub.dump(),
InputReader.dump(),InputDispatcher.dump()这3类,另外如果发生过input ANR,那么也会输出上一个ANR的状态.
其中mPendingEvent代表的当下正在处理的事件.