Watchdogpublic 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 void | addMonitor(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);
}
| void | checkMemory()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);
}
| void | checkReboot(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);
| void | collectGlobalMemory()Retrieve memory usage over all application processes. This is an
async operation, so must be done before doing memory checks.
mActivity.requestPss(mGlobalPssCollected);
| void | collectMemory()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 long | computeCalendarTime(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;
| void | computeMemcheckTimesLocked(long curTime)Compute the times during which we next would like to perform process
restarts.
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.Watchdog | getInstance()
if (sWatchdog == null) {
sWatchdog = new Watchdog();
}
return sWatchdog;
| public void | init(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();
| void | logGlobalMemory()
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 void | processStarted(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;
}
}
| void | rebootSystem(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 void | reportPss(com.android.server.Watchdog$PssRequestor req, java.lang.String name, int pss)
synchronized (this) {
if (mPhoneReq == req) {
mPhonePss = pss;
}
}
| void | retrieveBrutalityAmount()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 void | run()
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.String | shouldWeBeBrutalLocked(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.
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;
|
|