ANR源碼分析之InputDispatcher Timeout

  在上篇文章中,介紹ANR產生的原因、ANR的分類以及ANR問題的分析。本篇文章接下來將從源碼的角度來分析ANR產生的過程,首先介紹InputDispatcher Timeout產生的過程。在ANR產生時,最終都會調用到appNotResponding()方法,該方法在Android 7.0以前定義在ActivityManagerService.java類中,在Android 7.0中定義在AppErrors.java類中,本文將以Android 7.0源碼來分析ANR的產生過程。首先來分析appNotResponding()方法,在該方法中將記錄一些ANR的信息到event、system、trace日誌文件中,併發送應用未響應Dialog給系統顯示。ANR日誌分析將在下一個小節中描述,appNotResponding()在frameworks/base/services/core/java/com/android/server/am/AppErrors.java文件中實現:

/*
* 該函數的主要作用是記錄發生ANR的信息到日誌文件中,包括event日誌、system日誌以及trace日誌中,
* 同時在應用顯示一個ANR Dialog通知應用發生了ANR
*/
final void appNotResponding(ProcessRecord app, ActivityRecord activity,
            ActivityRecord parent, boolean aboveSystem, final String annotation) {
        //保存最近執行的進程號
        ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
        SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
         ...........
        //記錄發生ANR的時間
        long anrTime = SystemClock.uptimeMillis();
        if (ActivityManagerService.MONITOR_CPU_USAGE) {
            //第一次更新CPU的狀態
            mService.updateCpuStatsNow();
        }

        // Unless configured otherwise, swallow ANRs in background processes & kill the process.
        boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
                Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;

        boolean isSilentANR;
        //在system日誌中,打印ANR發生的原因
        synchronized (mService) {
            .........
            app.notResponding = true;

            //1.記錄ANR日誌到event日誌中
            EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
                    app.processName, app.info.flags, annotation);

            // 將應用pid添加到firstPids集合中
            firstPids.add(app.pid);

            isSilentANR = !showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID;
            if (!isSilentANR) {
                int parentPid = app.pid;
                if (parent != null && parent.app != null && parent.app.pid > 0) {
                    parentPid = parent.app.pid;
                }
                if (parentPid != app.pid) firstPids.add(parentPid);//添加父進程的pid到firstPids集合中

                if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);//再將當前應用的pid添加到firstPids集合中

                //將最近使用的進程pid添加到firstPids和lastPids集合中
                for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {
                    ProcessRecord r = mService.mLruProcesses.get(i);
                    if (r != null && r.thread != null) {
                        int pid = r.pid;
                        if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
                            if (r.persistent) {
                                firstPids.add(pid);
                                if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
                            } else {
                                lastPids.put(pid, Boolean.TRUE);
                                if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
                            }
                        }
                    }
                }
            }
        }


        // 2.記錄ANR信息到system日誌中
        StringBuilder info = new StringBuilder();
        info.setLength(0);
        info.append("ANR in ").append(app.processName);
        if (activity != null && activity.shortComponentName != null) {
            info.append(" (").append(activity.shortComponentName).append(")");
        }
        info.append("\n");
        info.append("PID: ").append(app.pid).append("\n");
        if (annotation != null) {
            info.append("Reason: ").append(annotation).append("\n");
        }
        if (parent != null && parent != activity) {
            info.append("Parent: ").append(parent.shortComponentName).append("\n");
        }

        ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);

        String[] nativeProcs = NATIVE_STACKS_OF_INTEREST;
        // 3.記錄ANR信息到trace日誌文件中
        File tracesFile = null;
        if (isSilentANR) {
            tracesFile = mService.dumpStackTraces(true, firstPids, null, lastPids,
                null);
        } else {
            //調用AMS的dumpStackTraces記錄ANR日誌到trace文件中
            tracesFile = mService.dumpStackTraces(true, firstPids, processCpuTracker, lastPids,
                nativeProcs);
        }

        String cpuInfo = null;
        if (ActivityManagerService.MONITOR_CPU_USAGE) {
            //第二次更新CPU的狀態
            mService.updateCpuStatsNow();
            synchronized (mService.mProcessCpuTracker) {
                //記錄第一次CPU的信息
                cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
            }

            info.append(processCpuTracker.printCurrentLoad());
            info.append(cpuInfo);
        }
        //記錄第二次CPU的信息
        info.append(processCpuTracker.printCurrentState(anrTime));

        //記錄ANR信息到system日誌中
        Slog.e(TAG, info.toString());
        if (tracesFile == null) {
            // There is no trace file, so dump (only) the alleged culprit's threads to the log
            //如果沒有生成trace文件,則發送SIGNAL_QUIT信號
            Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
        }

        //將ANR信息寫入到DropBox中
        mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
                cpuInfo, tracesFile, null);

        ........

        synchronized (mService) {

             ........
            //設置應用未相應狀態,並尋找錯誤的接收器
            makeAppNotRespondingLocked(app,
                    activity != null ? activity.shortComponentName : null,
                    annotation != null ? "ANR " + annotation : "ANR",
                    info.toString());


            // 4.通知系統顯示應用未響應的Dialog
            Message msg = Message.obtain();
            HashMap<String, Object> map = new HashMap<String, Object>();
            msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
            msg.obj = map;
            msg.arg1 = aboveSystem ? 1 : 0;
            map.put("app", app);
            if (activity != null) {
                map.put("activity", activity);
            }

            mService.mUiHandler.sendMessage(msg);
        }
    }
 從上面可以看到appNotResponding()方法主要做了4件事情:
  1.記錄ANR信息到event日誌中;
  2.記錄ANR信息到system日誌中,裏面包含了ANR發生時的CPU狀態信息;
  3.記錄ANR信息到trace日誌中,裏面包含了最近運行進程的方法調用堆棧信息;
  4.通知系統顯示應用未響應的Dialog;

 在上面介紹的4種ANR類型,都會最終調用到appNotResponding()方法,接下來分別介紹這4種ANR是如何產生的,以及如何最終調用到AppErrors的appNotResponding()方法。

一、 InputDispatcher Timeout

  當輸入事件例如觸摸、點擊事件,在5s內沒有響應的話,則會產生一個ANR消息。InputDispatcher Timeout的整體流程如下:


 應用接收到輸入事件時,會向系統註冊該輸入事件對應的輸入通道、輸入窗口等信息,InputDispatcher會監聽這些註冊的輸入事件。當輸入事件執行完成了,就會向系統發送處理完成finish消息。如果在5s內,沒有收到輸入事件結束finish消息,則InputDispatcher發送ANR通知。InputDispatcher註冊輸入通道信息的函數如下:
 frameworks/native/services/inputflinger/InputDispatcher.cpp
/*
 * 註冊輸入通道,並監聽該輸入通道對應的Socket fd。
 */
    status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {


    { // acquire lock
        AutoMutex _l(mLock);

        ...
        //創建連接
        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
        //返回連接的fd
        int fd = inputChannel->getFd();
        //添加連接到集合中
        mConnectionsByFd.add(fd, connection);

        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }
        //將fd添加到mLooper中,監聽該fd對應的輸入事件
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

    // Wake the looper because some connections have changed.
    //喚醒mLooper處理,連接已經改變了
    mLooper->wake();
    return OK;
}
 mLooper是Native Looper,在/system/core/libutils/Looper.cpp文件中實現,主要的作用是將輸入請求添加到請求隊列中。
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
    .......
    { // acquire lock
        AutoMutex _l(mLock);

        Request request;
        request.fd = fd;
        request.ident = ident;
        request.events = events;
        request.seq = mNextRequestSeq++;
        request.callback = callback;
        request.data = data;
        if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1

        struct epoll_event eventItem;
        request.initEventItem(&eventItem);

        ssize_t requestIndex = mRequests.indexOfKey(fd);
        //如果是首次添加
        if (requestIndex < 0) {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
            ........
            //將請求添加到請求隊列中
            mRequests.add(fd, request);
        } else {//非首次添加

            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
            .....
            //更新請求
            mRequests.replaceValueAt(requestIndex, request);
        }
    } // release lock
    return 1;
}
 當開始處理請求隊列中的輸入事件請求時,會回調該輸入事件註冊的回調函數handleReceiveCallback。
/*
* 輸入事件的回調函數
*/
int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
    InputDispatcher* d = static_cast<InputDispatcher*>(data);

    { // acquire lock
        AutoMutex _l(d->mLock);

        ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
        ....
        bool notify;
        sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
        if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
            ......
            nsecs_t currentTime = now();
            bool gotOne = false;
            status_t status;
            //死循環,等待輸入事件finish信號
            for (;;) {
                uint32_t seq;
                bool handled;
                //等待輸入事件finish信號
                status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
                if (status) {
                    break;
                }
                //結束當前輸入事件分發過程
                d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
                gotOne = true;
            }
            if (gotOne) {
                d->runCommandsLockedInterruptible();
                if (status == WOULD_BLOCK) {
                    return 1;
                }
            }
            ......

        } else {
           ........
        }

        // Unregister the channel.
        d->unregisterInputChannelLocked(connection->inputChannel, notify);
        return 0; // remove the callback
    } // release lock
}
 當開始處理輸入事件後,會循環等待輸入事件finish信號。如果接收到輸入事件的finish信號後,則結束當前事件的分發過程。如果在5s內,還未收到輸入事件的finish信號,則會調用InputDispatcher的handleTargetsNotReadyLocked()方法,發送ANR通知。

1.handleTargetsNotReadyLocked方法(InputDispatcher.cpp)
 handleTargetsNotReadyLocked方法主要的工作是獲取超時時間,然後設置輸入事件等待的原因,等待開始的時間、等待的超時時間。如果當前時間大於等待的超時時間,則說明發生了超時,調用onANRLocked發送ANR通知。
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) {
                timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
            } else if (applicationHandle != NULL) {
                timeout = applicationHandle->getDispatchingTimeout(
                        DEFAULT_INPUT_DISPATCHING_TIMEOUT);
            } else {
                timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
            }

            //設置等待的原因,等待開始的時間,等待超時的時間
            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;
    }

    //當前時間超過設定的超時時間
    if (currentTime >= mInputTargetWaitTimeoutTime) {
        onANRLocked(currentTime, applicationHandle, windowHandle,
                entry->eventTime, mInputTargetWaitStartTime, reason);


        *nextWakeupTime = LONG_LONG_MIN;
        return INPUT_EVENT_INJECTION_PENDING;
    } else {
       ........
    }
}
2.onANRLocked方法(InputDispatcher.cpp)
 在onANRLocked()方法中,主要是記錄ANR發生時的狀態,並將ANR日誌輸出到main日誌中。
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;
    //在main日誌中打印ANR信息
    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);

    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doNotifyANRLockedInterruptible);
    commandEntry->inputApplicationHandle = applicationHandle;
    commandEntry->inputWindowHandle = windowHandle;
    commandEntry->reason = reason;
}
3.doNotifyANRLockedInterruptible方法(InputDispatcher.cpp)
 doNotifyANRLockedInterruptible方法主要工作是從系統框架層獲取新的timeout,併發送ANR消息到框架層,最後恢復輸入事件狀態,並用新的timeout開始下一個輸入事件的超時處理。
void InputDispatcher::doNotifyANRLockedInterruptible(
        CommandEntry* commandEntry) {
    mLock.unlock();

    //獲取新的timeout,並通知應用ANR消息
    nsecs_t newTimeout = mPolicy->notifyANR(
            commandEntry->inputApplicationHandle, commandEntry->inputWindowHandle,
            commandEntry->reason);

    mLock.lock();
    //恢復狀態
    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
            commandEntry->inputWindowHandle != NULL
                    ? commandEntry->inputWindowHandle->getInputChannel() : NULL);
}
4.notifyANR方法(InputManagerService.java)
 在InputDispatcher.cpp中調用doNotifyANRLockedInterruptible()方法後,最終會調用到framework中的InputManagerService.java類中的notifyANR方法。notifyANR方法調用InputMonitor類中的notifyANR方法。
 frameworks/services/core/java/com/android/server/input/InputManagerService.java
/*
 * 本地方法回調,通知應用層輸入事件分發發生了ANR
 */

    private long notifyANR(InputApplicationHandle inputApplicationHandle,
            InputWindowHandle inputWindowHandle, String reason) {
        return mWindowManagerCallbacks.notifyANR(
                inputApplicationHandle, inputWindowHandle, reason);
    }
5.notifyANR方法(InputMonitor.java)
 notifyANR方法返回一個超時時間,並通知窗口管理器,發生了ANR。
 frameworks/services/core/java/com/android/server/wm/InputMonitor.java
 /*
  * 通知窗口管理器,應用沒有響應。
  * 返回一個新的超時時間,或者返回0來終止事件分發
  */
    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 = (WindowState) inputWindowHandle.windowState;
                if (windowState != null) {
                    appWindowToken = windowState.mAppToken;
                }
            }
            if (appWindowToken == null && inputApplicationHandle != null) {
                appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;
            }


            if (windowState != null) {
                Slog.i(TAG_WM, "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(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);
            }
            //保存ANR狀態
            mService.saveANRStateLocked(appWindowToken, windowState, reason);
        }

        if (appWindowToken != null && appWindowToken.appToken != null) {
            try {
                // Notify the activity manager about the timeout and let it decide whether
                // to abort dispatching or keep waiting.
                boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(reason);
                if (! abort) {
                    // The activity manager declined to abort dispatching.
                    // Wait a bit longer and timeout again later.
                    return appWindowToken.inputDispatchingTimeoutNanos;
                }
            } catch (RemoteException ex) {
            }
        } else if (windowState != null) {
            try {
                //通知Activity管理器超時時間,並讓它決定是否終止事件處理還是繼續等待
                long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut(
                        windowState.mSession.mPid, aboveSystem, reason);
                if (timeout >= 0) {
                    //Activity管理器拒絕終止事件分發,再等待一個更長的超時時間
                    return timeout * 1000000L; // nanoseconds
                }
            } catch (RemoteException ex) {
            }
        }
        //返回0表示終止事件的分發處理
        return 0; // abort dispatching
    }
6.inputDispatchingTimedOut方法(ActivityManagerService.java)
 inputDispatchingTimedOut主要是通過getInputDispatchingTimeoutLocked方法獲取超時時間,然後通過inputDispatchingTimedOut方法通知AMS,輸入事件分發超時了,發送ANR通知。
public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
        if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires permission "
                    + android.Manifest.permission.FILTER_EVENTS);
        }
        ProcessRecord proc;
        long timeout;
        synchronized (this) {
            synchronized (mPidsSelfLocked) {
                proc = mPidsSelfLocked.get(pid);
            }
            timeout = getInputDispatchingTimeoutLocked(proc);
        }

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

        return timeout;
    }

7.getInputDispatchingTimeoutLocked方法(ActivityManagerService.java)
 獲取輸入事件超時時間,這個時間在KEY_DISPATCHING_TIMEOUT中定義,爲5s中。
public static long getInputDispatchingTimeoutLocked(ProcessRecord r) {
        if (r != null && (r.instrumentationClass != null || r.usingWrapper)) {
            return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
        }
        return KEY_DISPATCHING_TIMEOUT;
    }

    //key dispatching超時時間,爲5s
    static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
8.inputDispatchingTimedOut(ActivityManagerService.java)
 發送ANR通知給AppErros類統一處理。AppErros在appNotResponding方法中統一處理ANR消息,在前面的小節中已經介紹了。
 /*
    * 處理輸入事件分發超時
    * 返回事件分發是否應該終止還是繼續
    */
    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) {
            ......
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    //通知AppErrors發生了ANR
                    mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation);
                }
            });
        }

        return true;
    }
 至此,已經介紹完了InputDispatcher Timeout產生的整個流程,可以看到InputDispatcher的超時時間爲5s鍾。接下來分析Broadcast Timeout產生的流程。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章