Android Input (8) -- ANR input event原理

Link:

Android Input (1) -- InputManagerService启动

Android Input (2) -- inputReader

Android Input (3) -- inputDispatcher

Android Input (4) -- inputDispatcher到ViewRootImpl

Android Input (5) -- ViewRootImpl 的事件分发(Activity ViewGroup View )

Android Input (6) -- PhoneWindowManager中Power,BACK等按键处理流程

Android Input (7) -- 模块的锁检测

Android Input (8) -- ANR input event原理


目录

1. ANR input事件原理位置

1.1 InputDispatcherThread threadLoop()

1.2 dispatchOnceInnerLocked

1.3 InputDispatcher::dispatchMotionLocked

1.4 InputDispatcher::findTouchedWindowTargetsLocked

1.5 handleTargetsNotReadyLocked

1.6 ANR时间总结

2. onANRLocked触发的ANR流程

2.1 InputDispatcher::onANRLocked

2.2 IMS.notifyANR

2.3 InputMonitor.notifyANR

2.5 mAppErrors.appNotResponding

3 ANR REASON 和input Drop resason

3.1 ANR分类

3.2 drop事件分类


 

1. ANR input事件原理位置

1.1 InputDispatcherThread threadLoop()

 

@frameworks/native/services/inputflinger/InputDispatcher.cpp
bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex _l(mLock);
        //唤醒等待线程,monitor()用于监控dispatcher是否发生死锁
        mDispatcherIsAliveCondition.broadcast();

        if (!haveCommandsLocked()) {  第一次haveCommandsLocked为false,执行dispatchOnceInnerLocked
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

    } // release lock
    ...
    mLooper->pollOnce(timeoutMillis);   //inputReader读取事件最后把事件放入minBoundQueue后,调用loop::wake唤醒inputDispatcher
}

bool InputDispatcher::haveCommandsLocked() const {
    return !mCommandQueue.isEmpty();
}

线程执行Looper->pollOnce,进入epoll_wait等待状态,当发生以下任一情况则退出等待状态:

  • callback:通过回调方法来唤醒;
  • timeout:到达nextWakeupTime时间,超时唤醒;
  • wake: 主动调用Looper的wake()方法;

 

1.2 dispatchOnceInnerLocked

 

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now(); //当前时间
    if (! mPendingEvent) {
        if (mInboundQueue.isEmpty()) {
            if (isAppSwitchDue) {
        } else {
            // Inbound queue has at least one entry.
            mPendingEvent = mInboundQueue.dequeueAtHead();   //取出mInboundQueue头部事件
        }
        // Poke user activity for this event.
        if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            pokeUserActivityLocked(mPendingEvent);
        }
        // Get ready to dispatch the event.
        resetANRTimeoutsLocked();   //重置ANR信息
    }

    case EventEntry::TYPE_KEY: {
        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
        if (isAppSwitchDue) {   //isAppSwitchDue  app切换延迟,当切换超时,则抢占分发,丢弃其他所有即将要处理的事件。
            if (isAppSwitchKeyEventLocked(typedEntry)) {
                resetPendingAppSwitchLocked(true);
                isAppSwitchDue = false;
            } else if (dropReason == DROP_REASON_NOT_DROPPED) {
                dropReason = DROP_REASON_APP_SWITCH;  //drop
            }
        }

        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);   //分发key按键事件
        break;
    }

    case EventEntry::TYPE_MOTION: {
        done = dispatchMotionLocked(currentTime, typedEntry, //分发motion触摸事件
                &dropReason, nextWakeupTime);
        break;
    }

    //分发操作完成,则进入该分支
    if (done) {
        if (dropReason != DROP_REASON_NOT_DROPPED) {
            dropInboundEventLocked(mPendingEvent, dropReason);
        }
        mLastDropReason = dropReason;

        releasePendingEventLocked();//释放pending事件
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
    }
}

在enqueueInboundEventLocked()的过程中已设置mAppSwitchDueTime等于eventTime加上500ms:

mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;

该方法主要功能:

1.mDispatchFrozen用于决定是否冻结事件分发工作不再往下执行;

2.当事件分发的时间点距离该事件加入mInboundQueue的时间超过500ms,则认为app切换过期,即isAppSwitchDue=true;

3.mInboundQueue不为空,则取出头部的事件,放入mPendingEvent变量;并重置ANR时间;

4.根据EventEntry的type类型分别处理,比如按键调用dispatchKeyLocked分发事件,触摸事件调用dispatchMotionLocked分发事件;再根据分发结果来决定是否进入done;

执行完成(done)的处理:

5.根据dropReason(默认NOT_DROPPED不处理)来决定是否丢失事件:dropInboundEventLocked

6.释放当前正在处理的事件(即mPendingEvent):releasePendingEventLocked

 

1.3 InputDispatcher::dispatchMotionLocked

//dispatchMotionLocked和dispatchKeyLocked最后都会调用dispatchEventLocked来分发事件

 

bool InputDispatcher::dispatchMotionLocked(
        nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ...
    bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
    // Identify targets.
    Vector<InputTarget> inputTargets;

    bool conflictingPointerActions = false;
    int32_t injectionResult;
    if (isPointerEvent) {   //点事件判断 findTouchedWindowTargetsLocked
        // Pointer event.  (eg. touchscreen)
        injectionResult = findTouchedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
    } else {
        // Non touch event.  (eg. trackball)
        injectionResult = findFocusedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime);
    }
    ...
    addMonitoringTargetsLocked(inputTargets);

    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

主要工作

  • 在dispatchEventLocked发送事件之前,会先去判断这个事件是点击事件(isPointEvent)还是其他事件--寻找目标窗口

分发事件

 

1.4 InputDispatcher::findTouchedWindowTargetsLocked

int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
        const MotionEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
        bool* outConflictingPointerActions) {
    enum InjectionPermission {
        INJECTION_PERMISSION_UNKNOWN,
        INJECTION_PERMISSION_GRANTED,
        INJECTION_PERMISSION_DENIED
    };
 
    nsecs_t startTime = now();
 
    ......
 
    // Ensure all touched foreground windows are ready for new input.
    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
        const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
        if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
            // Check whether the window is ready for more input.
            String8 reason = checkWindowReadyForMoreInputLocked(currentTime,
                    touchedWindow.windowHandle, entry, "touched");
            if (!reason.isEmpty()) {
                injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
                        NULL, touchedWindow.windowHandle, nextWakeupTime, reason.string());
                goto Unresponsive;
            }
        }
    }
    ......
    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
        const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
        addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                touchedWindow.pointerIds, inputTargets);
    }
    ......
    return injectionResult;
}
  • 这是一个很长的方法
  • 大体是判断这个事件的类型
  • 获取能够处理这个事件的forceground window,如果这个window不能够继续处理事件,就是说这个window的主线程被某些耗时操作占据,我们继续看handleTargetsNotReadyLocked这个方法。

1.5 handleTargetsNotReadyLocked

 

@InputDispatcher.cpp
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) {
    } else {
        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
            nsecs_t timeout;
            if (windowHandle != NULL) {
            } else {
                timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;  //5s
            }

            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
            mInputTargetWaitStartTime = currentTime;
            mInputTargetWaitTimeoutTime = currentTime + timeout;   //anr 5s时间
        }
    }

    if (currentTime >= mInputTargetWaitTimeoutTime) {
        onANRLocked(currentTime, applicationHandle, windowHandle,
                entry->eventTime, mInputTargetWaitStartTime, reason);   //onANRLocked
    } else {

    }
}

1.6 ANR时间总结

  • dispatchOnceInnerLocked设置currentTime,在handleTargetsNotReadyLocked说明窗口有问题后,设置mInputTargetWaitTimeoutTime = currentTime + timeout; //anr 5s时间

此时,假如currentTime >= mInputTargetWaitTimeoutTime相差大于5s则调用onANRLocked通知发生ANR

 

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now(); //当前时间
    
@InputDispatcher.cpp
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) {
    } else {
        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
            mInputTargetWaitStartTime = currentTime;
            mInputTargetWaitTimeoutTime = currentTime + timeout;   //anr 5s时间
        }
    }

    if (currentTime >= mInputTargetWaitTimeoutTime) {
        onANRLocked(currentTime, applicationHandle, windowHandle,
                entry->eventTime, mInputTargetWaitStartTime, reason);   //onANRLocked
    } else {

    }
}
  • 其中,在调用开始会恢复ANR TIMEOUT时间,比如这里会设置 mInputTargetWaitCause为INPUT_TARGET_WAIT_CAUSE_NONE,在上面发生ANR前如果有问题会设置 为INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,下一次进去时候,就不会走mInputTargetWaitTimeoutTime = currentTime + timeout,还是用上一次的时间,这样就仍然是一个事件
  • 因此ANR时间,是从findTouchedWindowTargetsLocked开始检测窗口到resetANRTimeoutsLocked 这段执行的时间,假如执行resetANRTimeoutsLocked就会恢复这些值,就不会判断下去了

 

void InputDispatcher::resetANRTimeoutsLocked() {
#if DEBUG_FOCUS
        ALOGD("Resetting ANR timeouts.");
#endif

    // Reset input target wait timeout.
    mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
    mInputTargetWaitApplicationHandle.clear();
}

2. onANRLocked触发的ANR流程

2.1 InputDispatcher::onANRLocked

 

void InputDispatcher::onANRLocked(
        nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
        const sp<InputWindowHandle>& windowHandle,
        nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) {

    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);    //ALOG   InputDispatcher:  Application is not responding

    //保存ANR信息 mLastANRState
    mLastANRState.clear();
    mLastANRState.append(INDENT "ANR:\n");
    mLastANRState.appendFormat(INDENT2 "Time: %s\n", timestr);
    mLastANRState.appendFormat(INDENT2 "Window: %s\n",
    ...
    dumpDispatchStateLocked(mLastANRState);    //dump 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,接下来看该方法。

 

void InputDispatcher::doNotifyANRLockedInterruptible(
        CommandEntry* commandEntry) {
    mLock.unlock();

    nsecs_t newTimeout = mPolicy->notifyANR(    //1 notifyANR
            commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle,
            commandEntry->reason);

    mLock.lock();

    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,     //2  resumeAfterTargetsNotReadyTimeoutLocked
            commandEntry->inputWindowHandle != NULL
                    ? commandEntry->inputWindowHandle->getInputChannel() : NULL);
}

2.2 IMS.notifyANR

mPolicy是指NativeInputManager对象 参考文章: Android Input -- inputReader

 

@frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
        const sp<InputWindowHandle>& inputWindowHandle, const String8& reason) {
    ...
    jlong newTimeout = env->CallLongMethod(mServiceObj,
                gServiceClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj,
                reasonObj);
}

//InputManagerService   notifyANR
int register_android_server_InputManager(JNIEnv* env) {
    int res = jniRegisterNativeMethods(env, "com/android/server/input/InputManagerService",
            gInputManagerMethods, NELEM(gInputManagerMethods));
    GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz,
            "notifyANR",
            "(Lcom/android/server/input/InputApplicationHandle;Lcom/android/server/input/InputWindowHandle;Ljava/lang/String;)J");

@frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
private long notifyANR(InputApplicationHandle inputApplicationHandle,
        InputWindowHandle inputWindowHandle, String reason) {
    return mWindowManagerCallbacks.notifyANR(
            inputApplicationHandle, inputWindowHandle, reason);
}

2.3 InputMonitor.notifyANR

打印WMS的anr log

 

@frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
final class InputMonitor implements InputManagerService.WindowManagerCallbacks {

@Override
public long notifyANR(InputApplicationHandle inputApplicationHandle,
        InputWindowHandle inputWindowHandle, String reason) {
    synchronized (mService.mWindowMap) {
        ...
        //输出input事件分发超时log   //TAG_WM = "WindowManager"
        if (windowState != null) {
            Slog.i(TAG_WM, "Input event dispatching timed out "
                    + "sending to " + windowState.mAttrs.getTitle()
                    + ".  Reason: " + reason);
            // Figure out whether this window is layered above system windows.
            // We need to do this here to help the activity manager know how to
            // layer its ANR dialog.
            int systemAlertLayer = mService.mPolicy.getWindowLayerFromTypeLw(
                    TYPE_APPLICATION_OVERLAY, windowState.mOwnerCanAddInternalSystemWindow);
            aboveSystem = windowState.mBaseLayer > systemAlertLayer;
        } else if (appWindowToken != null) {
            Slog.i(TAG_WM, "Input event dispatching timed out "
                    + "sending to application " + appWindowToken.stringName
                    + ".  Reason: " + reason);
        } else {
            Slog.i(TAG_WM, "Input event dispatching timed out "
                    + ".  Reason: " + reason);
        }

        mService.saveANRStateLocked(appWindowToken, windowState, reason);
    }

    // All the calls below need to happen without the WM lock held since they call into AM.
    mService.mAmInternal.saveANRState(reason);

    if (appWindowToken != null && appWindowToken.appToken != null) {
        // Notify the activity manager about the timeout and let it decide whether
        // to abort dispatching or keep waiting.
        final AppWindowContainerController controller = appWindowToken.getController();
        final boolean abort = controller != null
                && controller.keyDispatchingTimedOut(reason,
                        (windowState != null) ? windowState.mSession.mPid : -1);
        if (!abort) {
            // The activity manager declined to abort dispatching.
            // Wait a bit longer and timeout again later.
            return appWindowToken.mInputDispatchingTimeoutNanos;
        }
    } else if (windowState != null) {
        try {
            // Notify the activity manager about the timeout and let it decide whether
            // to abort dispatching or keep waiting.
            long timeout = ActivityManager.getService().inputDispatchingTimedOut(
                    windowState.mSession.mPid, aboveSystem, reason);
            if (timeout >= 0) {
                return timeout * 1000000L; // nanoseconds
            }
        } catch (RemoteException ex) {
        }
    }
    return 0; // abort dispatching
}

发生input相关的ANR时在system log输出ANR信息,并且tag为WindowManager. 主要有3类log:

  • Input event dispatching timed out sending to [windowState.mAttrs.getTitle()]
  • Input event dispatching timed out sending to application [appWindowToken.stringName)]
  • Input event dispatching timed out sending.

 

2.4 AMS.inputDispatchingTimedOut(proc ...)

 

// controller.keyDispatchingTimedOut会依次执行 AppWindowContainerController  -- AppWindowContainerListener -- ActivityRecord
/Bordrin_B31/hqx/LINUX/android/frameworks/base/services/core/java/com/android/server/wm/
H A D	AppWindowContainerListener.java	41 boolean keyDispatchingTimedOut(String reason, int windowPid); method in interface:AppWindowContainerListener 
H A D	AppWindowContainerController.java	782 boolean keyDispatchingTimedOut(String reason, int windowPid) { method in class:AppWindowContainerController 
@frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
@Override
public boolean keyDispatchingTimedOut(String reason, int windowPid) {
    ActivityRecord anrActivity;
    ProcessRecord anrApp;
    boolean windowFromSameProcessAsActivity;
    synchronized (service) {
        anrActivity = getWaitingHistoryRecordLocked();
        anrApp = app;
        windowFromSameProcessAsActivity =
                app == null || app.pid == windowPid || windowPid == -1;
    }
    if (windowFromSameProcessAsActivity) {
        return service.inputDispatchingTimedOut(anrApp, anrActivity, this, false, reason);
    } else {
        // In this case another process added windows using this activity token. So, we call the
        // generic service input dispatch timed out method so that the right process is blamed.
        return service.inputDispatchingTimedOut(windowPid, false /* aboveSystem */, reason) < 0;
    }
}

//最终 controller.keyDispatchingTimedOut 和 service.inputDispatchingTimedOut都会走到 AMS.inputDispatchingTimedOut(proc ...)

ActivityManager.getService().inputDispatchingTimedOut

 

@ActivityManagerService.java
public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
    ...
    synchronized (this) {
        synchronized (mPidsSelfLocked) {
            proc = mPidsSelfLocked.get(pid);
        }
        timeout = getInputDispatchingTimeoutLocked(proc);
    }

    if (inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
        return -1;
    }

    return timeout;
}

//就会调用AppErrors.appNotResponding,kill app进程,dumpStrace等

public boolean inputDispatchingTimedOut(final ProcessRecord proc,
        final ActivityRecord activity, final ActivityRecord parent,
        final boolean aboveSystem, String reason) {
        ...
    if (proc != null) {
        synchronized (this) {

        mHandler.post(new Runnable() {
            @Override
            public void run() {
                mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation);
            }
        });
    }
}

2.5 mAppErrors.appNotResponding

 

打印log,trace,cpu之类
    final void appNotResponding(ProcessRecord app, ActivityRecord activity,
            ActivityRecord parent, boolean aboveSystem, final String annotation) {
       ...
    }

3 ANR REASON 和input Drop resason

3.1 ANR分类

 

String8 InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
        const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
        const char* targetType) {
    // If the window is paused then keep waiting.
    if (windowHandle->getInfo()->paused) {
        return String8::format("Waiting because the %s window is paused.", targetType);
    }

    // If the window's connection is not registered then keep waiting.
    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);
    }

    // If the connection is dead then keep waiting.
    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 the connection is backed up then keep waiting.
    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());
    }

    // Ensure that the dispatch queues aren't too far backed up for this event.
    if (eventEntry->type == EventEntry::TYPE_KEY) {
        // 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 (!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 {
        // Touch events can always be sent to a window immediately because the user intended
        // to touch whatever was visible at the time.  Even if focus changes or a new
        // window appears moments later, the touch event was meant to be delivered to
        // whatever window happened to be on screen at the time.
        //
        // Generic motion events, such as trackball or joystick events are a little trickier.
        // Like key events, generic motion events are delivered to the focused window.
        // Unlike key events, generic motion events don't tend to transfer focus to other
        // windows and it is not important for them to be serialized.  So we prefer to deliver
        // generic motion events as soon as possible to improve efficiency and reduce lag
        // through batching.
        //
        // The one case where we pause input event delivery is when the wait queue is piling
        // up with lots of events because the application is not responding.
        // This condition ensures that ANRs are detected reliably.
        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();
}

由checkWindowReadyForMoreInputLocked完成, ANR reason主要有以下几类:

 

  1. 无窗口, 有应用:Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.
  2. 窗口暂停: Waiting because the [targetType] window is paused.
  3. 窗口未连接: Waiting because the [targetType] window’s input channel is not registered with the input dispatcher. The window may be in the process of being removed.
  4. 窗口连接已死亡:Waiting because the [targetType] window’s input connection is [Connection.Status]. The window may be in the process of being removed.
  5. 窗口连接已满:Waiting because the [targetType] window’s input channel is full. Outbound queue length: [outboundQueue长度]. Wait queue length: [waitQueue长度].
  6. 按键事件,输出队列或事件等待队列不为空: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长度].
  7. 非按键事件,事件等待队列不为空且头事件分发超时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”
  • 另外, findFocusedWindowTargetsLocked, findTouchedWindowTargetsLocked这两个方法中可以通过实现 updateDispatchStatisticsLocked()来分析anr问题.

 

3.2 drop事件分类

 

void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
    const char* reason;
    switch (dropReason) {
    case DROP_REASON_POLICY:
#if DEBUG_INBOUND_EVENT_DETAILS
        ALOGD("Dropped event because policy consumed it.");
#endif
        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:
        ALOG_ASSERT(false);
        return;
    }

    switch (entry->type) {
    case EventEntry::TYPE_KEY: {
        CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
        synthesizeCancelationEventsForAllConnectionsLocked(options);
        break;
    }
    case EventEntry::TYPE_MOTION: {
        MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
        if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
            CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
            synthesizeCancelationEventsForAllConnectionsLocked(options);
        } else {
            CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
            synthesizeCancelationEventsForAllConnectionsLocked(options);
        }
        break;
    }
    }
}

由 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的过程, 可以统计等待时长信息.

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值