FileDocCategorySizeDatePackage
Watchdog.javaAPI DocAndroid 1.5 API34393Wed May 06 22:42:00 BST 2009com.android.server

Watchdog

public class Watchdog extends Thread
This class calls its monitor every minute. Killing this process if they don't return

Fields Summary
static final String
TAG
static final boolean
localLOGV
static final boolean
DB
static final int
MONITOR
static final int
GLOBAL_PSS
static final int
TIME_TO_WAIT
static final int
EVENT_LOG_TAG
static final int
EVENT_LOG_PROC_PSS_TAG
static final int
EVENT_LOG_SOFT_RESET_TAG
static final int
EVENT_LOG_HARD_RESET_TAG
static final int
EVENT_LOG_PSS_STATS_TAG
static final int
EVENT_LOG_PROC_STATS_TAG
static final int
EVENT_LOG_SCHEDULED_REBOOT_TAG
static final int
EVENT_LOG_MEMINFO_TAG
static final int
EVENT_LOG_VMSTAT_TAG
static final int
EVENT_LOG_REQUESTED_REBOOT_TAG
static final int
MEMCHECK_DEFAULT_INTERVAL
static final int
MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL
static final int
MEMCHECK_DEFAULT_SYSTEM_SOFT_THRESHOLD
static final int
MEMCHECK_DEFAULT_SYSTEM_HARD_THRESHOLD
static final int
MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD
static final int
MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD
static final int
MEMCHECK_DEFAULT_EXEC_START_TIME
static final int
MEMCHECK_DEFAULT_EXEC_END_TIME
static final int
MEMCHECK_DEFAULT_MIN_SCREEN_OFF
static final int
MEMCHECK_DEFAULT_MIN_ALARM
static final int
MEMCHECK_DEFAULT_RECHECK_INTERVAL
static final int
REBOOT_DEFAULT_INTERVAL
static final int
REBOOT_DEFAULT_START_TIME
static final int
REBOOT_DEFAULT_WINDOW
static final String
CHECKUP_ACTION
static final String
REBOOT_ACTION
static Watchdog
sWatchdog
final android.os.Handler
mHandler
final Runnable
mGlobalPssCollected
final ArrayList
mMonitors
android.content.ContentResolver
mResolver
BatteryService
mBattery
PowerManagerService
mPower
AlarmManagerService
mAlarm
com.android.server.am.ActivityManagerService
mActivity
boolean
mCompleted
boolean
mForceKillSystem
Monitor
mCurrentMonitor
PssRequestor
mPhoneReq
int
mPhonePid
int
mPhonePss
long
mLastMemCheckTime
boolean
mHavePss
long
mLastMemCheckRealtime
boolean
mHaveGlobalPss
final MemMonitor
mSystemMemMonitor
final MemMonitor
mPhoneMemMonitor
final Calendar
mCalendar
long
mMemcheckLastTime
long
mMemcheckExecStartTime
long
mMemcheckExecEndTime
int
mMinScreenOff
int
mMinAlarm
boolean
mNeedScheduledCheck
android.app.PendingIntent
mCheckupIntent
android.app.PendingIntent
mRebootIntent
long
mBootTime
int
mRebootInterval
boolean
mReqRebootNoWait
int
mReqRebootInterval
int
mReqRebootStartTime
int
mReqRebootWindow
int
mReqMinScreenOff
int
mReqMinNextAlarm
int
mReqRecheckInterval
final PssStats
mPssStats
final String[]
mMemInfoFields
final long[]
mMemInfoSizes
final String[]
mVMStatFields
final long[]
mVMStatSizes
final long[]
mPrevVMStatSizes
long
mLastLogGlobalMemoryTime
Constructors Summary
private Watchdog()

        super("watchdog");
        mHandler = new HeartbeatHandler();
        mGlobalPssCollected = new GlobalPssCollected();
    
Methods Summary
public voidaddMonitor(com.android.server.Watchdog$Monitor monitor)

        synchronized (this) {
            if (isAlive()) {
                throw new RuntimeException("Monitors can't be added while the Watchdog is running");
            }
            mMonitors.add(monitor);
        }
    
voidcheckMemory()
Check memory usage in the system, scheduling kills/reboots as needed. This always runs on the mHandler thread.

        boolean needScheduledCheck;
        long curTime;
        long nextTime = 0;

        long recheckInterval = Settings.Gservices.getLong(
                mResolver, Settings.Gservices.MEMCHECK_RECHECK_INTERVAL,
                MEMCHECK_DEFAULT_RECHECK_INTERVAL) * 1000;

        mSystemMemMonitor.retrieveSettings(mResolver);
        mPhoneMemMonitor.retrieveSettings(mResolver);
        retrieveBrutalityAmount();

        synchronized (this) {
            curTime = System.currentTimeMillis();
            mNeedScheduledCheck = false;

            // How is the system doing?
            if (mSystemMemMonitor.checkLocked(curTime, Process.myPid(),
                    (int)Process.getPss(Process.myPid()))) {
                // Not good!  Time to suicide.
                mForceKillSystem = true;
                notifyAll();
                return;
            }

            // How is the phone process doing?
            if (mPhoneReq != null) {
                if (mPhoneMemMonitor.checkLocked(curTime, mPhonePid,
                        mPhonePss)) {
                    // Just kill the phone process and let it restart.
                    Process.killProcess(mPhonePid);
                }
            } else {
                mPhoneMemMonitor.clear();
            }

            needScheduledCheck = mNeedScheduledCheck;
            if (needScheduledCheck) {
                // Something is going bad, but now is not a good time to
                // tear things down...  schedule an alarm to check again soon.
                nextTime = curTime + recheckInterval;
                if (nextTime < mMemcheckExecStartTime) {
                    nextTime = mMemcheckExecStartTime;
                } else if (nextTime >= mMemcheckExecEndTime){
                    // Need to check during next exec time...  so that needs
                    // to be computed.
                    if (localLOGV) Log.v(TAG, "Computing next time range");
                    computeMemcheckTimesLocked(nextTime);
                    nextTime = mMemcheckExecStartTime;
                }

                if (localLOGV) {
                    mCalendar.setTimeInMillis(nextTime);
                    Log.v(TAG, "Next Alarm Time: " + mCalendar);
                }
            }
        }

        if (needScheduledCheck) {
            if (localLOGV) Log.v(TAG, "Scheduling next memcheck alarm for "
                    + ((nextTime-curTime)/1000/60) + "m from now");
            mAlarm.remove(mCheckupIntent);
            mAlarm.set(AlarmManager.RTC_WAKEUP, nextTime, mCheckupIntent);
        } else {
            if (localLOGV) Log.v(TAG, "No need to schedule a memcheck alarm!");
            mAlarm.remove(mCheckupIntent);
        }
    
voidcheckReboot(boolean fromAlarm)

        int rebootInterval = mReqRebootInterval >= 0 ? mReqRebootInterval
                : Settings.Gservices.getInt(
                mResolver, Settings.Gservices.REBOOT_INTERVAL,
                REBOOT_DEFAULT_INTERVAL);
        mRebootInterval = rebootInterval;
        if (rebootInterval <= 0) {
            // No reboot interval requested.
            if (localLOGV) Log.v(TAG, "No need to schedule a reboot alarm!");
            mAlarm.remove(mRebootIntent);
            return;
        }

        long rebootStartTime = mReqRebootStartTime >= 0 ? mReqRebootStartTime
                : Settings.Gservices.getLong(
                mResolver, Settings.Gservices.REBOOT_START_TIME,
                REBOOT_DEFAULT_START_TIME);
        long rebootWindowMillis = (mReqRebootWindow >= 0 ? mReqRebootWindow
                : Settings.Gservices.getLong(
                mResolver, Settings.Gservices.REBOOT_WINDOW,
                REBOOT_DEFAULT_WINDOW)) * 1000;
        long recheckInterval = (mReqRecheckInterval >= 0 ? mReqRecheckInterval
                : Settings.Gservices.getLong(
                mResolver, Settings.Gservices.MEMCHECK_RECHECK_INTERVAL,
                MEMCHECK_DEFAULT_RECHECK_INTERVAL)) * 1000;

        retrieveBrutalityAmount();

        long realStartTime;
        long now;

        synchronized (this) {
            now = System.currentTimeMillis();
            realStartTime = computeCalendarTime(mCalendar, now,
                    rebootStartTime);

            long rebootIntervalMillis = rebootInterval*24*60*60*1000;
            if (DB || mReqRebootNoWait ||
                    (now-mBootTime) >= (rebootIntervalMillis-rebootWindowMillis)) {
                if (fromAlarm && rebootWindowMillis <= 0) {
                    // No reboot window -- just immediately reboot.
                    EventLog.writeEvent(EVENT_LOG_SCHEDULED_REBOOT_TAG, now,
                            (int)rebootIntervalMillis, (int)rebootStartTime*1000,
                            (int)rebootWindowMillis, "");
                    rebootSystem("Checkin scheduled forced");
                    return;
                }

                // Are we within the reboot window?
                if (now < realStartTime) {
                    // Schedule alarm for next check interval.
                    realStartTime = computeCalendarTime(mCalendar,
                            now, rebootStartTime);
                } else if (now < (realStartTime+rebootWindowMillis)) {
                    String doit = shouldWeBeBrutalLocked(now);
                    EventLog.writeEvent(EVENT_LOG_SCHEDULED_REBOOT_TAG, now,
                            (int)rebootInterval, (int)rebootStartTime*1000,
                            (int)rebootWindowMillis, doit != null ? doit : "");
                    if (doit == null) {
                        rebootSystem("Checked scheduled range");
                        return;
                    }

                    // Schedule next alarm either within the window or in the
                    // next interval.
                    if ((now+recheckInterval) >= (realStartTime+rebootWindowMillis)) {
                        realStartTime = computeCalendarTime(mCalendar,
                                now + rebootIntervalMillis, rebootStartTime);
                    } else {
                        realStartTime = now + recheckInterval;
                    }
                } else {
                    // Schedule alarm for next check interval.
                    realStartTime = computeCalendarTime(mCalendar,
                            now + rebootIntervalMillis, rebootStartTime);
                }
            }
        }

        if (localLOGV) Log.v(TAG, "Scheduling next reboot alarm for "
                + ((realStartTime-now)/1000/60) + "m from now");
        mAlarm.remove(mRebootIntent);
        mAlarm.set(AlarmManager.RTC_WAKEUP, realStartTime, mRebootIntent);
    
voidcollectGlobalMemory()
Retrieve memory usage over all application processes. This is an async operation, so must be done before doing memory checks.

        mActivity.requestPss(mGlobalPssCollected);
    
voidcollectMemory()
Retrieve memory usage information from specific processes being monitored. This is an async operation, so must be done before doing memory checks.

        synchronized (this) {
            if (mPhoneReq != null) {
                mPhoneReq.requestPss();
            }
        }
    
static longcomputeCalendarTime(java.util.Calendar c, long curTime, long secondsSinceMidnight)


        // start with now
        c.setTimeInMillis(curTime);

        int val = (int)secondsSinceMidnight / (60*60);
        c.set(Calendar.HOUR_OF_DAY, val);
        secondsSinceMidnight -= val * (60*60);
        val = (int)secondsSinceMidnight / 60;
        c.set(Calendar.MINUTE, val);
        c.set(Calendar.SECOND, (int)secondsSinceMidnight - (val*60));
        c.set(Calendar.MILLISECOND, 0);

        long newTime = c.getTimeInMillis();
        if (newTime < curTime) {
            // The given time (in seconds since midnight) has already passed for today, so advance
            // by one day (due to daylight savings, etc., the delta may differ from 24 hours).
            c.add(Calendar.DAY_OF_MONTH, 1);
            newTime = c.getTimeInMillis();
        }

        return newTime;
    
voidcomputeMemcheckTimesLocked(long curTime)
Compute the times during which we next would like to perform process restarts.

param
curTime The current system time.

        if (mMemcheckLastTime == curTime) {
            return;
        }

        mMemcheckLastTime = curTime;

        long memcheckExecStartTime = Settings.Gservices.getLong(
                mResolver, Settings.Gservices.MEMCHECK_EXEC_START_TIME,
                MEMCHECK_DEFAULT_EXEC_START_TIME);
        long memcheckExecEndTime = Settings.Gservices.getLong(
                mResolver, Settings.Gservices.MEMCHECK_EXEC_END_TIME,
                MEMCHECK_DEFAULT_EXEC_END_TIME);

        mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime,
                memcheckExecEndTime);
        if (mMemcheckExecEndTime < curTime) {
            memcheckExecStartTime += 24*60*60;
            memcheckExecEndTime += 24*60*60;
            mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime,
                    memcheckExecEndTime);
        }
        mMemcheckExecStartTime = computeCalendarTime(mCalendar, curTime,
                memcheckExecStartTime);

        if (localLOGV) {
            mCalendar.setTimeInMillis(curTime);
            Log.v(TAG, "Current Time: " + mCalendar);
            mCalendar.setTimeInMillis(mMemcheckExecStartTime);
            Log.v(TAG, "Start Check Time: " + mCalendar);
            mCalendar.setTimeInMillis(mMemcheckExecEndTime);
            Log.v(TAG, "End Check Time: " + mCalendar);
        }
    
public static com.android.server.WatchdoggetInstance()

    

        
        if (sWatchdog == null) {
            sWatchdog = new Watchdog();
        }

        return sWatchdog;
    
public voidinit(android.content.Context context, BatteryService battery, PowerManagerService power, AlarmManagerService alarm, com.android.server.am.ActivityManagerService activity)

        mResolver = context.getContentResolver();
        mBattery = battery;
        mPower = power;
        mAlarm = alarm;
        mActivity = activity;

        context.registerReceiver(new CheckupReceiver(),
                new IntentFilter(CHECKUP_ACTION));
        mCheckupIntent = PendingIntent.getBroadcast(context,
                0, new Intent(CHECKUP_ACTION), 0);

        context.registerReceiver(new RebootReceiver(),
                new IntentFilter(REBOOT_ACTION));
        mRebootIntent = PendingIntent.getBroadcast(context,
                0, new Intent(REBOOT_ACTION), 0);

        context.registerReceiver(new RebootRequestReceiver(),
                new IntentFilter(Intent.ACTION_REBOOT),
                android.Manifest.permission.REBOOT, null);

        mBootTime = System.currentTimeMillis();
    
voidlogGlobalMemory()


      
        PssStats stats = mPssStats;
        mActivity.collectPss(stats);
        EventLog.writeEvent(EVENT_LOG_PSS_STATS_TAG,
                stats.mEmptyPss, stats.mEmptyCount,
                stats.mBackgroundPss, stats.mBackgroundCount,
                stats.mServicePss, stats.mServiceCount,
                stats.mVisiblePss, stats.mVisibleCount,
                stats.mForegroundPss, stats.mForegroundCount,
                stats.mNoPssCount);
        EventLog.writeEvent(EVENT_LOG_PROC_STATS_TAG,
                stats.mProcDeaths[0], stats.mProcDeaths[1], stats.mProcDeaths[2],
                stats.mProcDeaths[3], stats.mProcDeaths[4]);
        Process.readProcLines("/proc/meminfo", mMemInfoFields, mMemInfoSizes);
        for (int i=0; i<mMemInfoSizes.length; i++) {
            mMemInfoSizes[i] *= 1024;
        }
        EventLog.writeEvent(EVENT_LOG_MEMINFO_TAG,
                (int)mMemInfoSizes[0], (int)mMemInfoSizes[1], (int)mMemInfoSizes[2],
                (int)mMemInfoSizes[3], (int)mMemInfoSizes[4],
                (int)mMemInfoSizes[5], (int)mMemInfoSizes[6], (int)mMemInfoSizes[7],
                (int)mMemInfoSizes[8], (int)mMemInfoSizes[9], (int)mMemInfoSizes[10]);
        long now = SystemClock.uptimeMillis();
        long dur = now - mLastLogGlobalMemoryTime;
        mLastLogGlobalMemoryTime = now;
        Process.readProcLines("/proc/vmstat", mVMStatFields, mVMStatSizes);
        for (int i=0; i<mVMStatSizes.length; i++) {
            long v = mVMStatSizes[i];
            mVMStatSizes[i] -= mPrevVMStatSizes[i];
            mPrevVMStatSizes[i] = v;
        }
        EventLog.writeEvent(EVENT_LOG_VMSTAT_TAG, dur,
                (int)mVMStatSizes[0], (int)mVMStatSizes[1], (int)mVMStatSizes[2],
                (int)mVMStatSizes[3], (int)mVMStatSizes[4]);
    
public voidprocessStarted(com.android.server.Watchdog$PssRequestor req, java.lang.String name, int pid)

        synchronized (this) {
            if ("com.android.phone".equals(name)) {
                mPhoneReq = req;
                mPhonePid = pid;
                mPhonePss = 0;
            }
        }
    
voidrebootSystem(java.lang.String reason)
Perform a full reboot of the system.

        Log.i(TAG, "Rebooting system because: " + reason);
        try {
            android.os.Power.reboot(reason);
        } catch (IOException e) {
            Log.e(TAG, "Reboot failed!", e);
        }
    
public voidreportPss(com.android.server.Watchdog$PssRequestor req, java.lang.String name, int pss)

        synchronized (this) {
            if (mPhoneReq == req) {
                mPhonePss = pss;
            }
        }
    
voidretrieveBrutalityAmount()
Load the current Gservices settings for when {@link #shouldWeBeBrutalLocked} will allow the brutality to happen. Must not be called with the lock held.

        mMinScreenOff = (mReqMinScreenOff >= 0 ? mReqMinScreenOff
                : Settings.Gservices.getInt(
                mResolver, Settings.Gservices.MEMCHECK_MIN_SCREEN_OFF,
                MEMCHECK_DEFAULT_MIN_SCREEN_OFF)) * 1000;
        mMinAlarm = (mReqMinNextAlarm >= 0 ? mReqMinNextAlarm
                : Settings.Gservices.getInt(
                mResolver, Settings.Gservices.MEMCHECK_MIN_ALARM,
                MEMCHECK_DEFAULT_MIN_ALARM)) * 1000;
    
public voidrun()

        while (true) {
            mCompleted = false;
            mHandler.sendEmptyMessage(MONITOR);

            synchronized (this) {
                long timeout = TIME_TO_WAIT;

                // NOTE: We use uptimeMillis() here because we do not want to increment the time we
                // wait while asleep. If the device is asleep then the thing that we are waiting
                // to timeout on is asleep as well and won't have a chance to run. Causing a false
                // positive on when to kill things.
                long start = SystemClock.uptimeMillis();
                do {
                    try {
                        wait(timeout);
                    } catch (InterruptedException e) {
                        if (SystemProperties.getBoolean("ro.secure", false)) {
                            // If this is a secure build, just log the error.
                            Log.e("WatchDog", "Woof! Woof! Interrupter!");
                        } else {
                            throw new AssertionError("Someone interrupted the watchdog");
                        }
                    }
                    timeout = TIME_TO_WAIT - (SystemClock.uptimeMillis() - start);
                } while (timeout > 0 && !mForceKillSystem);

                if (mCompleted && !mForceKillSystem) {
                    // The monitors have returned.
                    continue;
                }
            }

            // If we got here, that means that the system is most likely hung.
            // First send a SIGQUIT so that we can see where it was hung. Then
            // kill this process so that the system will restart.
            String name = (mCurrentMonitor != null) ? mCurrentMonitor.getClass().getName() : "null";
            EventLog.writeEvent(EVENT_LOG_TAG, name);
            Process.sendSignal(Process.myPid(), Process.SIGNAL_QUIT);

            // Wait a bit longer before killing so we can make sure that the stacks are captured.
            try {
                Thread.sleep(10*1000);
            } catch (InterruptedException e) {
            }

            // Only kill the process if the debugger is not attached.
            if (!Debug.isDebuggerConnected()) {
                Process.killProcess(Process.myPid());
            }
        }
    
java.lang.StringshouldWeBeBrutalLocked(long curTime)
Determine whether it is a good time to kill, crash, or otherwise plunder the current situation for the overall long-term benefit of the world.

param
curTime The current system time.
return
Returns null if this is a good time, else a String with the text of why it is not a good time.

        if (mBattery == null || !mBattery.isPowered()) {
            return "battery";
        }

        if (mMinScreenOff >= 0 && (mPower == null ||
                mPower.timeSinceScreenOn() < mMinScreenOff)) {
            return "screen";
        }

        if (mMinAlarm >= 0 && (mAlarm == null ||
                mAlarm.timeToNextAlarm() < mMinAlarm)) {
            return "alarm";
        }

        return null;