安卓InputDispatching Timeout ANR 流程
- 1 ANR的检测逻辑有两个参与者: 观测者A和被观测者B,当然,这两者是不在同一个线程中的。
- 2 A在调用B中的逻辑时,同时在A中保存一个标记F,然后做个延时操作C,延时时间设为T,这一步称为: 埋雷 。
- 3 B中的逻辑如果被执行到,就会通知A去清除标记F,并且通知A解除C,这一步称为: 拆雷 。
- 4 如果C没被拆除,那么在时间T后就会被触发,就会去检测标记F是否还在,如果在,就说明B没有在指定的时间T内完成,那么就提示B发生了ANR,这一步称为: 爆雷 。
- 5 由于A和B是在不同线程中的,所以B即使死循环,也不会影响C的检测过程。
所以,我们可以将ANR更精炼的总结为: 埋雷、拆雷和爆雷三个步骤 。
InputDispatching Timeout: 输入事件(包括按键和触屏事件)在5秒内无响应,就会弹出 ANR 提示框,供用户选择继续等待程序响应或者关闭这个应用程序(也就是杀掉这个应用程序的进程)。输入超时类的 ANR 可以细分为以下两类:
1)处理消息超时: 这一类是指因为消息处理超时而发生的 ANR,在 log,会看到 “Input dispatching timed out (Waiting because the focused window has not finished processing
the input events that were previously delivered to it.)”
2)无法获取焦点: 这一类通常因为新窗口创建慢或旧窗口退出慢而造成窗口无法获得焦点从而发生 ANR,典型 Log “Reason: Waiting because no window has focus but there is a focused application
that may eventually add a window when it finishes starting up.”
这里不分析没有焦点的情形,只分析处理消息超时流程
在 input子系统中,正常逻辑是:InputDispatcher 负责将输入事件分发给 UI 主线程。UI主线程接收到输入事件后,使用 InputConsumer 来处理事件。经过一系列的 InputStage 完成事件分发后,执行finishInputEvent() 方法来告知 InputDispatcher 事件已经处理完成。InputDispatcher 中使用handleReceiveCallback() 方法来处理 UI 主线程返回的消息,最终将 dispatchEntry事件从等待队列中移除。
http://aospxref.com/android-13.0.0_r3/xref/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
3975 void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
// 打开下列的开关,会打印对应的log
3976 if (DEBUG_INBOUND_EVENT_DETAILS) {
3977 ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
3978 "displayId=%" PRId32 ", policyFlags=0x%x, "
3979 "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
3980 "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
3981 "yCursorPosition=%f, downTime=%" PRId64,
3982 args->id, args->eventTime, args->deviceId, args->source, args->displayId,
3983 args->policyFlags, args->action, args->actionButton, args->flags, args->metaState,
3984 args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision,
3985 args->xCursorPosition, args->yCursorPosition, args->downTime);
3986 for (uint32_t i = 0; i < args->pointerCount; i++) {
。。。。
4047 // Just enqueue a new motion event.// 创建 MotionEntry 对象
4048 std::unique_ptr<MotionEntry> newEntry =
4049 std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId,
4050 args->source, args->displayId, policyFlags,
4051 args->action, args->actionButton, args->flags,
4052 args->metaState, args->buttonState,
4053 args->classification, args->edgeFlags,
4054 args->xPrecision, args->yPrecision,
4055 args->xCursorPosition, args->yCursorPosition,
4056 args->downTime, args->pointerCount,
4057 args->pointerProperties, args->pointerCoords);
4058
4059 if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
4060 IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER &&
4061 !mInputFilterEnabled) {
4062 const bool isDown = args->action == AMOTION_EVENT_ACTION_DOWN;
4063 mLatencyTracker.trackListener(args->id, isDown, args->eventTime, args->readTime);
4064 }
4065
// 将 MotionEntry 事件保存到 mInboundQueue 队列中;如果保存之前 mInboundQueue是为空的,则 needWake 为true。
4066 needWake = enqueueInboundEventLocked(std::move(newEntry));
4067 mLock.unlock();
4068 } // release lock
4069 // 这里分析直接去唤醒inputdispatcher 线程
4070 if (needWake) {
4071 mLooper->wake();
4072 }
4073 }
InputDispatcher 是个线程,会循环执行 dispatchOnce 方法
602 void InputDispatcher::dispatchOnce() {
603 nsecs_t nextWakeupTime = LONG_LONG_MAX;// 局部空间,获取同步锁
604 { // acquire lock
605 std::scoped_lock _l(mLock);
606 mDispatcherIsAlive.notify_all();
607
608 // Run a dispatch loop if there are no pending commands.
609 // The dispatch loop might enqueue commands to run afterwards.// 如果没有 mCommandQueue ,没有 command 命令的话,
// 则执行 dispatchOnceInnerLocked,获取到下一次唤醒的时间:nextWakeupTime
// 1)如果有motion 事件,调用 dispatchOnceInnerLocked 方法
610 if (!haveCommandsLocked()) {
611 dispatchOnceInnerLocked(&nextWakeupTime);
612 }
613 // 执行command 命令,如果有的话,直接返回 true,然后马上唤醒线程
616 if (runCommandsLockedInterruptable()) {
617 nextWakeupTime = LONG_LONG_MIN;
618 }
619 // 处理anr 或者获取到下一次anr 的时间
// 2)获取超时anr 的时间:processAnrsLocked
622 const nsecs_t nextAnrCheck = processAnrsLocked();
623 nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
624
625 // We are about to enter an infinitely long sleep, because we have no commands or
626 // pending or queued events// 如果唤醒时间为最大,则表明没有input 事件,是idle 空闲状态
627 if (nextWakeupTime == LONG_LONG_MAX) {
628 mDispatcherEnteredIdle.notify_all();
629 }
630 } // release lock
631
632 // Wait for callback or timeout or wake. (make sure we round up, not down)
633 nsecs_t currentTime = now();
// 获取到唤醒线程的时间
// 调用 mLooper->wake 函数也可以唤醒线程
634 int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
635 mLooper->pollOnce(timeoutMillis);
636 }
1)如果有motion 事件,调用 dispatchOnceInnerLocked 方法
718 void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
719 nsecs_t currentTime = now();
。。。
// 初始 mPendingEvent 为null
746 if (!mPendingEvent) {
// 前面notifymotion 增加了 mInboundQueue
747 if (mInboundQueue.empty()) {
。。。
770 } else {
771 // Inbound queue has at least one entry.
// 这里去获取到motion 事件
772 mPendingEvent = mInboundQueue.front();
773 mInboundQueue.pop_front();
774 traceInboundQueueLengthLocked();
775 }
776
777 // Poke user activity for this event.
778 if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
779 pokeUserActivityLocked(*mPendingEvent);
780 }
781 }
。。。
798 switch (mPendingEvent->type) {
868 case EventEntry::Type::MOTION: {
869 std::shared_ptr<MotionEntry> motionEntry =
870 std::static_pointer_cast<MotionEntry>(mPendingEvent);
871 if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
872 dropReason = DropReason::APP_SWITCH;
873 }
874 if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {
875 dropReason = DropReason::STALE;
876 }
877 if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
878 dropReason = DropReason::BLOCKED;
879 }
// dispatchMotionLocked 方法去分发motion 事件
880 done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
881 break;
882 }
。。。
// 一般是返回true,表示分发motion 事件到应用了
902 if (done) {
903 if (dropReason != DropReason::NOT_DROPPED) {
904 dropInboundEventLocked(*mPendingEvent, dropReason);
905 }
906 mLastDropReason = dropReason;
907
// 返回true,则重新设置 mPendingEvent 为null
908 releasePendingEventLocked();
// 设置 nextWakeupTime 为最小,马上唤醒
909 *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
910 }
911 }
// dispatchMotionLocked 方法去分发motion 事件
1634 bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
1635 DropReason* dropReason, nsecs_t* nextWakeupTime) {
// 会打印对应的trace
1636 ATRACE_CALL();
。。。
1652 const bool isPointerEvent = isFromSource(entry->source, AINPUT_SOURCE_CLASS_POINTER);
1653
1654 // Identify targets.
1655 std::vector<InputTarget> inputTargets;
1656
1657 bool conflictingPointerActions = false;
1658 InputEventInjectionResult injectionResult;
1659 if (isPointerEvent) {
1660 // Pointer event. (eg. touchscreen)// 去根据xy 的坐标点,去找到触摸的 window
1661 injectionResult =
1662 findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
1663 &conflictingPointerActions);
。。。
// 增加到全局触摸事件
1687 addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
1688
1689 // Dispatch the motion.
1690 if (conflictingPointerActions) {
1691 CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
1692 "conflicting pointer actions");
1693 synthesizeCancelationEventsForAllConnectionsLocked(options);
1694 }
// 分发event
1695 dispatchEventLocked(currentTime, entry, inputTargets);
1696 return true;
1697 }
// 分发event
1753 void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
1754 std::shared_ptr<EventEntry> eventEntry,
1755 const std::vector<InputTarget>& inputTargets) {
1756 ATRACE_CALL();
1757 if (DEBUG_DISPATCH_CYCLE) {
1758 ALOGD("dispatchEventToCurrentInputTargets");
1759 }
1760
1761 updateInteractionTokensLocked(*eventEntry, inputTargets);
1762
1763 ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
1764
1765 pokeUserActivityLocked(*eventEntry);
1766
1767 for (const InputTarget& inputTarget : inputTargets) {// 找到对应的窗口的 connection
1768 sp<Connection> connection =
1769 getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
1770 if (connection != nullptr) {
1771 prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
1772 } else {
1773 if (DEBUG_FOCUS) {
1774 ALOGD("Dropping event delivery to target with channel '%s' because it "
1775 "is no longer registered with the input dispatcher.",
1776 inputTarget.inputChannel->getName().c_str());
1777 }
1778 }
1779 }
1780 }
准备分发事件给应用 prepareDispatchCycleLocked
2887 void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
2888 const sp<Connection>& connection,
2889 std::shared_ptr<EventEntry> eventEntry,
2890 const InputTarget& inputTarget) {
2891 if (ATRACE_ENABLED()) {
2892 std::string message =
2893 StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, id=0x%" PRIx32 ")",
2894 connection->getInputChannelName().c_str(), eventEntry->id);
2895 ATRACE_NAME(message.c_str());
2896 }
。。。。。
2944
2945 // Not splitting. Enqueue dispatch entries for the event as is.
2946 enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
2947 }
2949 void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
2950 const sp<Connection>& connection,
2951 std::shared_ptr<EventEntry> eventEntry,
2952 const InputTarget& inputTarget) {
2953 if (ATRACE_ENABLED()) {
2954 std::string message =
2955 StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, id=0x%" PRIx32 ")",
2956 connection->getInputChannelName().c_str(), eventEntry->id);
2957 ATRACE_NAME(message.c_str());
2958 }
2959
2960 bool wasEmpty = connection->outboundQueue.empty();
2961
// 将触摸事件保存到 outboundQueue 中
2962 // Enqueue dispatch entries for the requested modes.
2963 enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
2964 InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
2965 enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
2966 InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
2967 enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
2968 InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
2969 enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
2970 InputTarget::FLAG_DISPATCH_AS_IS);
2971 enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
2972 InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
2973 enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
2974 InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
2975
2976 // If the outbound queue was previously empty, start the dispatch cycle going.
2977 if (wasEmpty && !connection->outboundQueue.empty()) {
// 开始socket 通信去分发给应用 startDispatchCycleLocked
2978 startDispatchCycleLocked(currentTime, connection);
2979 }
2980 }
// 开始socket 通信去分发给应用 startDispatchCycleLocked
3214 void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
3215 const sp<Connection>& connection) {
3216 if (ATRACE_ENABLED()) {
3217 std::string message = StringPrintf("startDispatchCycleLocked(inputChannel=%s)",
3218 connection->getInputChannelName().c_str());
3219 ATRACE_NAME(message.c_str());
3220 }
3221 if (DEBUG_DISPATCH_CYCLE) {
3222 ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
3223 }
3224
3225 while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) {
3226 DispatchEntry* dispatchEntry = connection->outboundQueue.front();
3227 dispatchEntry->deliveryTime = currentTime;
// 获取到应用设置的超时事件,一般为 5 秒
3228 const std::chrono::nanoseconds timeout = getDispatchingTimeoutLocked(connection);
// 设置timeout 的时间
3229 dispatchEntry->timeoutTime = currentTime + timeout.count();
。。。
3252 case EventEntry::Type::MOTION: {
3253 const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
。。。
// socket 通信发送给 应用,给到应用去处理了
3285 // Publish the motion event.
3286 status = connection->inputPublisher
3287 .publishMotionEvent(dispatchEntry->seq,
3288 dispatchEntry->resolvedEventId,
3289 motionEntry.deviceId, motionEntry.source,
3290 motionEntry.displayId, std::move(hmac),
3291 dispatchEntry->resolvedAction,
3292 motionEntry.actionButton,
3293 dispatchEntry->resolvedFlags,
3294 motionEntry.edgeFlags, motionEntry.metaState,
3295 motionEntry.buttonState,
3296 motionEntry.classification,
3297 dispatchEntry->transform,
3298 motionEntry.xPrecision, motionEntry.yPrecision,
3299 motionEntry.xCursorPosition,
3300 motionEntry.yCursorPosition,
3301 dispatchEntry->rawTransform,
3302 motionEntry.downTime, motionEntry.eventTime,
3303 motionEntry.pointerCount,
3304 motionEntry.pointerProperties, usingCoords);
3305 break;
3306 }
。。。。。
3383 // Re-enqueue the event on the wait queue.
// 将触摸事件从 outboundQueue 移除掉
3384 connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),
3385 connection->outboundQueue.end(),
3386 dispatchEntry));
3387 traceOutboundQueueLength(*connection);
// 将其增加到 waitQueue 等待队列中
3388 connection->waitQueue.push_back(dispatchEntry);
// 如果应用没有die,则 将anr 超时时间保存到 mAnrTracker 中,还有对应的应用token
3389 if (connection->responsive) {
3390 mAnrTracker.insert(dispatchEntry->timeoutTime,
3391 connection->inputChannel->getConnectionToken());
3392 }
3393 traceWaitQueueLength(*connection);
3394 }
3395 }
2)获取超时anr 的时间:processAnrsLocked
622 const nsecs_t nextAnrCheck = processAnrsLocked();
// nextWakeupTime 由前面的分析,返回的是为 LONG_LONG_MIN,所以这里的唤醒时间为马上唤醒
623 nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
670 nsecs_t InputDispatcher::processAnrsLocked() {
671 const nsecs_t currentTime = now();
672 nsecs_t nextAnrCheck = LONG_LONG_MAX;// 下列是处理no focus 没有焦点的情况,这里不满足
674 if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
675 if (currentTime >= *mNoFocusedWindowTimeoutTime) {
676 processNoFocusedWindowAnrLocked();
677 mAwaitedFocusedApplication.reset();
678 mNoFocusedWindowTimeoutTime = std::nullopt;
679 return LONG_LONG_MIN;
680 } else {
681 // Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes.
682 nextAnrCheck = *mNoFocusedWindowTimeoutTime;
683 }
684 }
685
// 获取到前面保存到anr 的时间,即前面当前的时间 + timeout 5 秒
687 nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
// 当前的时间是小于 nextAnrCheck,所以返回 nextAnrCheck的时间
688 if (currentTime < nextAnrCheck) { // most likely scenario
689 return nextAnrCheck; // everything is normal. Let's check again at nextAnrCheck
690 }
691
692 // If we reached here, we have an unresponsive connection.
693 sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
694 if (connection == nullptr) {
695 ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());
696 return nextAnrCheck;
697 }
698 connection->responsive = false;
699 // Stop waking up for this unresponsive connection
700 mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
701 onAnrLocked(connection);
702 return LONG_LONG_MIN;
703 }
但是接下有个处理 :这里返回时 MIN 的,即马上唤醒。所以再走一次线程dispatchOnce 方法
nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
602 void InputDispatcher::dispatchOnce() {
603 nsecs_t nextWakeupTime = LONG_LONG_MAX;
604 { // acquire lock
605 std::scoped_lock _l(mLock);
606 mDispatcherIsAlive.notify_all();
607 // 这时候没有inboundqueue 了
610 if (!haveCommandsLocked()) {
611 dispatchOnceInnerLocked(&nextWakeupTime);
612 }
613
// 也没有cammand
616 if (runCommandsLockedInterruptable()) {
617 nextWakeupTime = LONG_LONG_MIN;
618 }
619
// 这里就可以获取到 anr 的时间了
622 const nsecs_t nextAnrCheck = processAnrsLocked();
623 nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
624
625 // We are about to enter an infinitely long sleep, because we have no commands or
626 // pending or queued events
627 if (nextWakeupTime == LONG_LONG_MAX) {
628 mDispatcherEnteredIdle.notify_all();
629 }
630 } // release lock
631
633 nsecs_t currentTime = now();
634 int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
// 这里去等待anr 超时的时间
635 mLooper->pollOnce(timeoutMillis);
636 }
3)排雷过程
// 在inputdispatcher 与应用创建连接的时候,会设置socket 通信的callback,执行 handleReceiveCallback 方法
5450 Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
5451 if (DEBUG_CHANNEL_CREATION) {
5452 ALOGD("channel '%s' ~ createInputChannel", name.c_str());
5453 }
。。。5475 std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
5476 this, std::placeholders::_1, token);
5477
5478 mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
5479 } // release lock
5480
5481 // Wake the looper because some connections have changed.
5482 mLooper->wake();
5483 return clientChannel;
5484 }
如果应用有发送socket 消息,则执行 handleReceiveCallback 方法
int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {std::scoped_lock _l(mLock);
// 通过token 获取到对应的connectionsp<Connection> connection = getConnectionLocked(connectionToken);bool notify;
// event 不能为 ALOOPER_EVENT_ERROR 或者 ALOOPER_EVENT_HANGUPif (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {if (!(events & ALOOPER_EVENT_INPUT)) {ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. ""events=0x%x",connection->getInputChannelName().c_str(), events);return 1;}nsecs_t currentTime = now();bool gotOne = false;status_t status = OK;
// 循环操作for (;;) {
// 通过socket 获取到消息Result<InputPublisher::ConsumerResponse> result =connection->inputPublisher.receiveConsumerResponse();if (!result.ok()) {status = result.error().code();break;}
// 如果result 是 Finished,则执行 finishDispatchCycleLockedif (std::holds_alternative<InputPublisher::Finished>(*result)) {const InputPublisher::Finished& finish =std::get<InputPublisher::Finished>(*result);finishDispatchCycleLocked(currentTime, connection, finish.seq, finish.handled,finish.consumeTime);
。。。
// 设置 gotOne = true
gotOne = true;}if (gotOne) {
// finishDispatchCycleLocked 增加了command,这里去执行 commandrunCommandsLockedInterruptable();if (status == WOULD_BLOCK) {return 1;}}
// 如果result 是 Finished,则执行 finishDispatchCycleLocked
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection, uint32_t seq,bool handled, nsecs_t consumeTime) {if (DEBUG_DISPATCH_CYCLE) {ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",connection->getInputChannelName().c_str(), seq, toString(handled));}if (connection->status == Connection::Status::BROKEN ||connection->status == Connection::Status::ZOMBIE) {return;}// Notify other system components and prepare to start the next dispatch cycle.auto command = [this, currentTime, connection, seq, handled, consumeTime]() REQUIRES(mLock) {doDispatchCycleFinishedCommand(currentTime, connection, seq, handled, consumeTime);};postCommandLocked(std::move(command));
}
前面 runCommandsLockedInterruptable 方法会去执行 doDispatchCycleFinishedCommand 方法
void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,const sp<Connection>& connection, uint32_t seq,bool handled, nsecs_t consumeTime) {// Handle post-event policy actions.std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);if (dispatchEntryIt == connection->waitQueue.end()) {return;}DispatchEntry* dispatchEntry = *dispatchEntryIt;const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
// 如果应用处理超过 2 秒,则会打印下列的日志if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());}
。。。
// 从等待队列中找到 dispatchEntryIt
dispatchEntryIt = connection->findWaitQueueEntry(seq);if (dispatchEntryIt != connection->waitQueue.end()) {dispatchEntry = *dispatchEntryIt;connection->waitQueue.erase(dispatchEntryIt);const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
// 从anrtracker 中移除这个超时时间,和connectionmAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);if (!connection->responsive) {connection->responsive = isConnectionResponsive(*connection);if (connection->responsive) {// The connection was unresponsive, and now it's responsive.processConnectionResponsiveLocked(*connection);}}traceWaitQueueLength(*connection);if (restartEvent && connection->status == Connection::Status::NORMAL) {connection->outboundQueue.push_front(dispatchEntry);traceOutboundQueueLength(*connection);} else {releaseDispatchEntry(dispatchEntry);}}// Start the next dispatch cycle for this connection.
// 如果还有下一个事件的话,则调用 startDispatchCycleLocked 继续去处理startDispatchCycleLocked(now(), connection);
}
则下次执行线程的时候,由于mAnrTracker移除了对应的conenction,则 processAnrsLocked不会触发anr 流程
4)爆雷过程
在anr 时间点触发线程执行:dispatchOnce,然后执行 processAnrsLocked 方法
nsecs_t InputDispatcher::processAnrsLocked() {const nsecs_t currentTime = now();nsecs_t nextAnrCheck = LONG_LONG_MAX;
// 不满足下列的条件if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {if (currentTime >= *mNoFocusedWindowTimeoutTime) {processNoFocusedWindowAnrLocked();mAwaitedFocusedApplication.reset();mNoFocusedWindowTimeoutTime = std::nullopt;return LONG_LONG_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());
// 这里当前的时间点是大于 anr 的时间,即前面没有移除if (currentTime < nextAnrCheck) { // most likely scenarioreturn nextAnrCheck; // everything is normal. Let's check again at nextAnrCheck}// 执行下列anr 的流程// If we reached here, we have an unresponsive connection.sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());if (connection == nullptr) {ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());return nextAnrCheck;}
// 设置应用回复为 falseconnection->responsive = false;// Stop waking up for this unresponsive connection
// 从anrtracker 中移除,防止进入到线程又再次触发anrmAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
// 走anr 流程:onAnrLockedonAnrLocked(connection);return LONG_LONG_MIN;
}
// 走anr 流程:onAnrLocked
void InputDispatcher::onAnrLocked(const sp<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;}
// 从等待队列中获取DispatchEntry* oldestEntry = *connection->waitQueue.begin();
// 获取到等待的时间const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
// 设置input dispatcher 时间分发超时的原因 reasonstd::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();
// 保存到dump 中updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);processConnectionUnresponsiveLocked(*connection, std::move(reason));// Stop waking up for events on this connection, it is already unresponsivecancelEventsForAnrLocked(connection);
}
processConnectionUnresponsiveLocked 处理anr
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));
}
void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token,std::optional<int32_t> pid,std::string reason) {
// 将command 保存到queue 中,因为前面会直接返回 MIN,所以线程会马山处理auto command = [this, token, pid, reason = std::move(reason)]() REQUIRES(mLock) {scoped_unlock unlock(mLock);mPolicy->notifyWindowUnresponsive(token, pid, reason);};postCommandLocked(std::move(command));
}
// 通知到界面没有反应 notifyWindowUnresponsive
/frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
// 前面将 NativeInputManager 保存到 了InputManager中
831 void NativeInputManager::notifyWindowUnresponsive(const sp<IBinder>& token,
832 std::optional<int32_t> pid,
833 const std::string& reason) {
834 #if DEBUG_INPUT_DISPATCHER_POLICY
835 ALOGD("notifyWindowUnresponsive");
836 #endif
// 会打印对应的trace:notifyWindowUnresponsive
837 ATRACE_CALL();
838
839 JNIEnv* env = jniEnv();
840 ScopedLocalFrame localFrame(env);
841
842 jobject tokenObj = javaObjectForIBinder(env, token);
843 ScopedLocalRef<jstring> reasonObj(env, env->NewStringUTF(reason.c_str()));
844
// 调用java 层的方法
845 env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyWindowUnresponsive, tokenObj,
846 pid.value_or(0), pid.has_value(), reasonObj.get());
847 checkAndClearExceptionFromCallback(env, "notifyWindowUnresponsive");
848 }
/frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
2927 // Native callback
2928 @SuppressWarnings("unused")
2929 private void notifyWindowUnresponsive(IBinder token, int pid, boolean isPidValid,
2930 String reason) {
2931 mWindowManagerCallbacks.notifyWindowUnresponsive(token,
2932 isPidValid ? OptionalInt.of(pid) : OptionalInt.empty(), reason);
2933 }
/frameworks/base/services/core/java/com/android/server/wm/InputManagerCallback.java
102 @Override
103 public void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,
104 @NonNull String reason) {
105 mService.mAnrController.notifyWindowUnresponsive(token, pid, reason);
106 }
/frameworks/base/services/core/java/com/android/server/wm/AnrController.java
88 void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,
89 @NonNull String reason) {
90 if (notifyWindowUnresponsive(token, reason)) {
91 return;
92 }
93 if (!pid.isPresent()) {
94 Slog.w(TAG_WM, "Failed to notify that window token=" + token + " was unresponsive.");
95 return;
96 }
97 notifyWindowUnresponsive(pid.getAsInt(), reason);
98 }
99
100 /**
101 * Notify a window identified by its input token was unresponsive.
102 *
103 * @return true if the window was identified by the given input token and the request was
104 * handled, false otherwise.
105 */
106 private boolean notifyWindowUnresponsive(@NonNull IBinder inputToken, String reason) {
107 preDumpIfLockTooSlow();
108 final int pid;
109 final boolean aboveSystem;
110 final ActivityRecord activity;
111 synchronized (mService.mGlobalLock) {
112 InputTarget target = mService.getInputTargetFromToken(inputToken);
113 if (target == null) {
114 return false;
115 }
116 WindowState windowState = target.getWindowState();
117 pid = target.getPid();
118 // Blame the activity if the input token belongs to the window. If the target is
119 // embedded, then we will blame the pid instead.
120 activity = (windowState.mInputChannelToken == inputToken)
121 ? windowState.mActivityRecord : null;// 会打印下列的log
122 Slog.i(TAG_WM, "ANR in " + target + ". Reason:" + reason);
123 aboveSystem = isWindowAboveSystem(windowState);
124 dumpAnrStateLocked(activity, windowState, reason);
125 }
// ActivityRecord 不为空
126 if (activity != null) {
127 activity.inputDispatchingTimedOut(reason, pid);
128 } else {
129 mService.mAmInternal.inputDispatchingTimedOut(pid, aboveSystem, reason);
130 }
131 return true;
132 }
/frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
6649 public boolean inputDispatchingTimedOut(String reason, int windowPid) {
6650 ActivityRecord anrActivity;
6651 WindowProcessController anrApp;
6652 boolean blameActivityProcess;
6653 synchronized (mAtmService.mGlobalLock) {
6654 anrActivity = getWaitingHistoryRecordLocked();
6655 anrApp = app;
6656 blameActivityProcess = hasProcess()
6657 && (app.getPid() == windowPid || windowPid == INVALID_PID);
6658 }
6659
6660 if (blameActivityProcess) {
6661 return mAtmService.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
6662 anrActivity.shortComponentName, anrActivity.info.applicationInfo,
6663 shortComponentName, app, false, reason);
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
17647 boolean inputDispatchingTimedOut(ProcessRecord proc, String activityShortComponentName,
17648 ApplicationInfo aInfo, String parentShortComponentName,
17649 WindowProcessController parentProcess, boolean aboveSystem, String reason) {
17650 if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
17651 throw new SecurityException("Requires permission " + FILTER_EVENTS);
17652 }
17653
17654 final String annotation;
17655 if (reason == null) {
17656 annotation = "Input dispatching timed out";
// 这里增加 Input dispatching timed out
17657 } else {
17658 annotation = "Input dispatching timed out (" + reason + ")";
17659 }
17660
17661 if (proc != null) {
17662 synchronized (this) {
17663 if (proc.isDebugging()) {
17664 return false;
17665 }
17666
17667 if (proc.getActiveInstrumentation() != null) {
17668 Bundle info = new Bundle();
17669 info.putString("shortMsg", "keyDispatchingTimedOut");
17670 info.putString("longMsg", annotation);
17671 finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);
17672 return true;
17673 }
17674 }// 这里去弹框
17675 mAnrHelper.appNotResponding(proc, activityShortComponentName, aInfo,
17676 parentShortComponentName, parentProcess, aboveSystem, annotation);
17677 }
17678
17679 return true;
17680 }
相关文章:
安卓InputDispatching Timeout ANR 流程
1 ANR的检测逻辑有两个参与者: 观测者A和被观测者B,当然,这两者是不在同一个线程中的。2 A在调用B中的逻辑时,同时在A中保存一个标记F,然后做个延时操作C,延时时间设为T,这一步称为: 埋雷 。3 B中的逻辑如果…...

【Nginx从入门到精通】03 、安装部署-让虚拟机可以联网
文章目录 总结一、配置联网【Minimal 精简版】1.1、查看网络配置1.2、配置ip地址 : 修改配置文件 <font colororange>ifcfg-ens33Stage 1:输入指令Stage 2:修改参数Stage 3:重启网络Stage 4:测试上网 二、配置联网【Everyth…...
java 增强型for循环 详解
Java 增强型 for 循环(Enhanced for Loop)详解 增强型 for 循环(也称为 “for-each” 循环)是 Java 从 JDK 5 开始引入的一种便捷循环语法,旨在简化对数组或集合类的迭代操作。 1. 基本语法 语法格式 for (类型 变量…...

浪潮云启操作系统(InLinux) bcache宕机问题分析
前言 本文以一次真实的内核宕机问题为切入点,结合实际操作案例,详细展示了如何利用工具 crash对内核转储(kdump)进行深入分析和调试的方法。通过对崩溃日志的解读、函数调用栈的梳理、关键地址的定位以及代码逻辑的排查ÿ…...

038集——quadtree(CAD—C#二次开发入门)
效果如下: using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using System; using System.Collections.Generic; using System.Linq; using System.T…...
备赛蓝桥杯--算法题目(1)
1. 链表求和 . - 力扣(LeetCode) class Solution { public:ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {ListNode *head nullptr, *tail nullptr;int carry 0;while (l1 || l2) {int n1 l1 ? l1->val: 0;int n2 l2 ? l2->val:…...
机器学习100道经典面试题库(二)
机器学习100道经典面试题库(31-60) 在大规模的语料中,挖掘词的相关性是一个重要的问题。以下哪一个信息不能用于确定两个词的相关性。 A、互信息 B、最大熵 C、卡方检验 D、最大似然比 答案:B 解析:最大熵代表了…...
Unet++改进37:添加KACNConvNDLayer(2024最新改进方法)
本文内容:添加KACNConvNDLayer 目录 论文简介 1.步骤一 2.步骤二 3.步骤三 4.步骤四 论文简介 1.步骤一 新建block/kacn_conv.py文件,添加如下代码: import torch import torch.nn as nn##源码地址:https://github.com/SynodicMonth/ChebyKAN class KACNConvNDLaye…...
基于 Levenberg - Marquardt 法的 BP 网络学习改进算法详解
基于 Levenberg - Marquardt 法的 BP 网络学习改进算法详解 一、引言 BP(Back Propagation)神经网络在众多领域有着广泛应用,但传统 BP 算法存在收敛速度慢、易陷入局部最优等问题。Levenberg - Marquardt(LM)算法作…...
MySQL 8.0与PostgreSQL 15.8的性能对比
根据搜索结果,以下是MySQL 8.0与PostgreSQL 15.8的性能对比: MySQL 8.0性能特点: MySQL在处理大量读操作时表现出色,其存储引擎InnoDB提供了行级锁定和高效的事务处理,适用于并发读取的场景。MySQL通过查询缓存来提高读…...
qt连接postgres数据库时 setConnectOptions函数用法
连接选项,而这些选项没有直接的方法对应,你可能需要采用以下策略之一: 由于Qt SQL API的限制,你可能需要采用一些变通方法或查阅相关文档和社区资源以获取最新的信息和最佳实践。如果你确实需要设置特定的连接选项,并且…...

MySQL45讲 第二十七讲 主库故障应对:从库切换策略与 GTID 详解——阅读总结
文章目录 MySQL45讲 第二十七讲 主库故障应对:从库切换策略与 GTID 详解一、一主多从架构与主备切换的挑战(一)一主多从基本结构(二)主备切换的复杂性 二、基于位点的主备切换(一)同步位点的概念…...

JavaWeb笔记整理——Spring Task、WebSocket
目录 SpringTask cron表达式 WebSocket SpringTask cron表达式 WebSocket...

基于SpringBoot+RabbitMQ完成应⽤通信
前言: 经过上面俩章学习,我们已经知道Rabbit的使用方式RabbitMQ 七种工作模式介绍_rabbitmq 工作模式-CSDN博客 RabbitMQ的工作队列在Spring Boot中实现(详解常⽤的⼯作模式)-CSDN博客作为⼀个消息队列,RabbitMQ也可以⽤作应⽤程…...
Flutter踩坑记录(一)debug运行生成的项目,不能手动点击运行
问题 IOS14设备,切后台划掉,二次启动崩溃。 原因 IOS14以上 flutter 不支持debugger模式下的二次启动 。 要二次启动需要以release方式编译工程安装至手机。 操作步骤 清理项目:在命令行中运行flutter clean来清理之前的构建文件。重新构…...
React的hook✅
为什么hook必须在组件内的顶层声明? 这是为了确保每次组件渲染时,Hooks 的调用顺序保持一致。React利用 hook 的调用顺序来跟踪各个 hook 的状态。每当一个函数组件被渲染时,所有的 hook 调用都是按照从上到下的顺序依次执行的。React 内部会…...

2024.5 AAAiGLaM:通过邻域分区和生成子图编码对领域知识图谱对齐的大型语言模型进行微调
GLaM: Fine-Tuning Large Language Models for Domain Knowledge Graph Alignment via Neighborhood Partitioning and Generative Subgraph Encoding 问题 如何将特定领域知识图谱直接整合进大语言模型(LLM)的表示中,以提高其在图数据上自…...
从熟练Python到入门学习C++(record 6)
基础之基础之最后一节-结构体 1.结构体的定义 结构体相对于自定义的一种新的变量类型。 四种定义方式,推荐第一种;第四种适合大量定义,也适合查找; #include <iostream> using namespace std; #include <string.h>…...

jenkins的安装(War包安装)
Jenkins是一个开源的持续集成工具,基于Java开发,主要用于监控持续的软件版本发布和测试项目。 它提供了一个开放易用的平台,使软件项目能够实现持续集成。Jenkins的功能包括持续的软件版本发布和测试项目,以及监控外部调用执行…...

WPS 加载项开发说明wpsjs
wpsjs几个常用的CMD命令: 1.打开cmd输入命令测试版本号 npm -v 2.首次安装nodejs,npm默认国外镜像,包下载较慢时,可切换到国内镜像 //下载速度较慢时可切换国内镜像 npm config set registry https://registry.npmmirror.com …...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...