从源码角度看 WatchDog 机制

简介

Android系统中,普通开发者最常见的还是ANR,即AMS中的四大组件没有响应,这个问题常常是由于APP自身质量问题,在主线程中做耗时的操作导致。那么不知道大家有没有想过,ANR是Android Framework对普通应用的约束,不允许应用为所欲为的随意在重要的线程做耗时的任务,以免导致用户体验过差,那么运行在system_server进程中的服务呢,也可以无拘无束么?

看过我先前写过的文章的读者应该都知道,四大组件中的大部分操作是需要应用和system_server进程的服务交互来完成的,比方说启动一个Activity,那么不仅仅需要应用端调用onCreate, onStart这些方法,同时也需要在AMS服务端完成创建ActivityRecord、管理Stack和Task的操作。如果说此时类似AMS这类重要的服务或者线程出现了因为异常导致的耗时与阻塞,用户不是也会有糟糕的体验么?这时系统会作出什么样的回应来应对这种情况呢?Android Framework当然考虑到了这一点,引入了WatchDog机制,用来监测所有的系统服务和各种重要的系统线程,如果这些中的任何一个出现超时导致系统hang机,WatchDog线程将会重启手机系统

本篇文章,我将从先列出WatchDog机制涉及的主要类并对每个类进行大概的解释,随后再列举出主要的被监测服务与线程,方便大家参考用;最后带大家走入细节分析源码,每个重要步骤的源码最后都会放出一张逻辑图,不想看源码的读者可以直接参考这些图

整体类图

查看大图

  • WatchDog: 本身是一个线程,在SystemServer.startOtherServices中被启动,启动之后直接进入一个无限循环,监测系统的重要服务和线程的状态;这个类是WatchDog机制的大管家,以单例的形式存在在系统
  • HandlerChecker: 在WatchDog被实例化时,会初始化几个重要线程的监测对象,它是以线程和Handler一一对应存在的,WatchDog维护着多个HandlerChecker, 当触发监测操作时,实际上是HandlerChecker实例进行实际操作的
  • Monitor: 被各种需要监测的服务实现的接口,当monitor方法迟迟不返回时,就会判定这个服务已经超时

WatchDog监控职责

WatchDog主要监测着系统中的重要服务和重要线程,如果其中的服务没有回应或者说线程被阻塞,那么就会触发WatchDog重启

系统Service

系统Service 职务 判断是否超时方法
ActivityManagerService 四大组件与进程的管理 ActivityManagerService.class类锁是否有被释放
InputManagerService 输入管理 mInputFilerLock是否有被释放
WindowManagerService 窗口管理 mWindowMap是否有被释放
PowerManagerService 电源管理 mLock是否有被释放
MountService 设备存储连接与管理服务 内部两个连接器的mDaemonLock锁是否被都被释放
NetworkmanagermentService 网络服务 内部连接器的mDaemonLock锁是否被释放

以上只列举出比较常见的服务,还有一些没有列举出来,判断服务是否有做超时的方式其实很简单,就是判断这个服务中的重要锁是否一直在阻塞状态,如果长时间没有拿到这个锁的持有权,那么也就意味着这个服务一直在做某项耗时的操作,迟迟没有返回。

WatchDog采用这样的方式来监听系统服务中的超时现象,也是因为Android系统中的确定义了很多重要的锁,列如AMS,PMS这两把类锁。这种方式即方便但却又是致命的。例如系统常用的四大组件,每个运行中的APP都是有可能掺和一脚进来的,如果某个APP做了恶意的耗时操作并且频繁请求,那么系统只要稍微考虑不周,就可能会对这几把系统服务的大锁造成影响,进而牵一发而动全身,导致系统卡顿频繁

值得一提的是,WatchDog监控者几个重要系统服务之外,也监控着system_server Binder线程池的情况。WatchDog的内部类通过调用native方法blockUntilThreadAvailable来进行实现,如果所有Binder线程始终不能被访问到,那么也会导致系统出现WatchDog异常

系统重要线程

线程 优先级 作用
android.fg THREAD\_PRIORITY\_DEFAULT 系统专用的线程,可以用来做比较耗时的任务
system_server 主线程 THREAD\_PRIORITY\_FOREGROUND 用来做system_serverUI操作
android.ui THREAD\_PRIORITY\_FOREGROUND 适合用来做很快就能完成的操作
android.io THREAD\_PRIORITY\_DEFAULT 用来做IO操作的线程
android.display THREAD\_PRIORITY\_DISPLAY 用来做与显示有关的任务,常常被WindowManager, WindowManager等使用
PackageManager THREAD_PRIORITY_BACKGROUND 用来做PMS相关的操作
ActivityManagerService THREAD_PRIORITY_FOREGROUND 用来做AMS相关的操作
PowerManagerService THREAD_PRIORITY_DISPLAY 用来做PowerManagerService相关的操作

源码分析

初始化

[frameworks/base/services/core/java/com/android/server/Watchdog.java]

private Watchdog() {
    super("watchdog");

    mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
            "foreground thread", DEFAULT_TIMEOUT);
    mHandlerCheckers.add(mMonitorChecker);
    mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
            "main thread", DEFAULT_TIMEOUT));
    mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
            "ui thread", DEFAULT_TIMEOUT));
    mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
            "i/o thread", DEFAULT_TIMEOUT));
    mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
            "display thread", DEFAULT_TIMEOUT));

    addMonitor(new BinderThreadMonitor());
}

public void addMonitor(Monitor monitor) {
    synchronized (this) {
        if (isAlive()) {
            throw new RuntimeException("Monitors can't be added once the Watchdog is running");
        }
        mMonitorChecker.addMonitor(monitor);
    }
}

public void addThread(Handler thread) {
    addThread(thread, DEFAULT_TIMEOUT);
}

WatchDog在被初始化会创建多个HandlerChecker并保存在mHandlerCheckers这个队列中,通过调用addMonitor系统服务监测monitor都会被添加到mMonitorChecker这个成员变量中的列表中。通过调用addThread可以检测指定的线程是否长期不可用。

[frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java]

public ActivityManagerService(Context systemContext) {
...

    Watchdog.getInstance().addMonitor(this);
    Watchdog.getInstance().addThread(mHandler);
}

以上代码为ActivityManagerService在初始化时,将自己添加到WatchDog监测之列,同时也监测着自己内部常用的线程情况

检查机制

[frameworks/base/services/core/java/com/android/server/Watchdog.java]

```java
@Override
public void run() {
boolean waitedHalf = false;
// 进入无限循环,监测系统状态
while (true) {
final ArrayList blockedCheckers;

    synchronized (this) {
        long timeout = CHECK_INTERVAL;
        // 触发检查
        for (int i=0; i<mHandlerCheckers.size(); i++) {
            HandlerChecker hc = mHandlerCheckers.get(i);
            hc.scheduleCheckLocked();
        }

        long start = SystemClock.uptimeMillis();
        while (timeout > 0) {
            try {
                // 等待监测完成
                wait(timeout);
            } catch (InterruptedException e) {
                Log.wtf(TAG, e);
            }

            timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
        }

        final int waitState = evaluateCheckerCompletionLocked();
        if (waitState == COMPLETED) {
            waitedHalf = false;
            continue;
        } else if (waitState == WAITING) {
            continue;
        } else if (waitState == WAITED_HALF) {
            if (!waitedHalf) {
                ArrayList<Integer> pids = new ArrayList<Integer>();
                pids.add(Process.myPid());
                // 如果已经超时了一半,那么会将system_server进程堆栈信息进行打印
                ActivityManagerService.dumpStackTraces(true, pids, null, null,
                        NATIVE_STACKS_OF_INTEREST);
                waitedHalf = true;
            }
            continue;
        }

        // 走到这里说明系统进入hang机状态了
        blockedCheckers = getBlockedCheckersLocked();
        subject = describeCheckersLocked(blockedCheckers);
        allowRestart = mAllowRestart;
top Created with Sketch.