blob: 68bf4fbb0653bd69e27c518a5fa04bb904656ae9 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import com.android.server.am.ActivityManagerService;
20
21import android.app.AlarmManager;
22import android.app.PendingIntent;
23import android.content.BroadcastReceiver;
24import android.content.ContentResolver;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.os.Debug;
29import android.os.Handler;
30import android.os.Message;
31import android.os.Process;
32import android.os.SystemClock;
33import android.os.SystemProperties;
34import android.provider.Settings;
35import android.util.Config;
36import android.util.EventLog;
37import android.util.Log;
38
39import java.io.IOException;
40import java.util.ArrayList;
41import java.util.Calendar;
42
43/** This class calls its monitor every minute. Killing this process if they don't return **/
44public class Watchdog extends Thread {
45 static final String TAG = "Watchdog";
46 static final boolean localLOGV = false || Config.LOGV;
47
48 // Set this to true to use debug default values.
49 static final boolean DB = false;
50
51 static final int MONITOR = 2718;
52 static final int GLOBAL_PSS = 2719;
53
54 static final int TIME_TO_WAIT = DB ? 15*1000 : 60*1000;
55 static final int EVENT_LOG_TAG = 2802;
56 static final int EVENT_LOG_PROC_PSS_TAG = 2803;
57 static final int EVENT_LOG_SOFT_RESET_TAG = 2804;
58 static final int EVENT_LOG_HARD_RESET_TAG = 2805;
59 static final int EVENT_LOG_PSS_STATS_TAG = 2806;
60 static final int EVENT_LOG_PROC_STATS_TAG = 2807;
61 static final int EVENT_LOG_SCHEDULED_REBOOT_TAG = 2808;
62 static final int EVENT_LOG_MEMINFO_TAG = 2809;
63 static final int EVENT_LOG_VMSTAT_TAG = 2810;
64 static final int EVENT_LOG_REQUESTED_REBOOT_TAG = 2811;
65
66 static final int MEMCHECK_DEFAULT_INTERVAL = DB ? 30 : 30*60; // 30 minutes
67 static final int MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL = DB ? 60 : 2*60*60; // 2 hours
68 static final int MEMCHECK_DEFAULT_SYSTEM_SOFT_THRESHOLD = (DB ? 10:16)*1024*1024; // 16MB
69 static final int MEMCHECK_DEFAULT_SYSTEM_HARD_THRESHOLD = (DB ? 14:20)*1024*1024; // 20MB
70 static final int MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD = (DB ? 4:8)*1024*1024; // 8MB
71 static final int MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD = (DB ? 8:12)*1024*1024; // 12MB
72
73 static final int MEMCHECK_DEFAULT_EXEC_START_TIME = 1*60*60; // 1:00am
74 static final int MEMCHECK_DEFAULT_EXEC_END_TIME = 5*60*60; // 5:00am
75 static final int MEMCHECK_DEFAULT_MIN_SCREEN_OFF = DB ? 1*60 : 5*60; // 5 minutes
76 static final int MEMCHECK_DEFAULT_MIN_ALARM = DB ? 1*60 : 3*60; // 3 minutes
77 static final int MEMCHECK_DEFAULT_RECHECK_INTERVAL = DB ? 1*60 : 5*60; // 5 minutes
78
79 static final int REBOOT_DEFAULT_INTERVAL = DB ? 1 : 0; // never force reboot
80 static final int REBOOT_DEFAULT_START_TIME = 3*60*60; // 3:00am
81 static final int REBOOT_DEFAULT_WINDOW = 60*60; // within 1 hour
82
83 static final String CHECKUP_ACTION = "com.android.service.Watchdog.CHECKUP";
84 static final String REBOOT_ACTION = "com.android.service.Watchdog.REBOOT";
85
86 static Watchdog sWatchdog;
87
88 /* This handler will be used to post message back onto the main thread */
89 final Handler mHandler;
90 final Runnable mGlobalPssCollected;
91 final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
92 ContentResolver mResolver;
93 BatteryService mBattery;
94 PowerManagerService mPower;
95 AlarmManagerService mAlarm;
96 ActivityManagerService mActivity;
97 boolean mCompleted;
98 boolean mForceKillSystem;
99 Monitor mCurrentMonitor;
100
101 PssRequestor mPhoneReq;
102 int mPhonePid;
103 int mPhonePss;
104
105 long mLastMemCheckTime = -(MEMCHECK_DEFAULT_INTERVAL*1000);
106 boolean mHavePss;
107 long mLastMemCheckRealtime = -(MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL*1000);
108 boolean mHaveGlobalPss;
109 final MemMonitor mSystemMemMonitor = new MemMonitor("system",
110 Settings.Gservices.MEMCHECK_SYSTEM_ENABLED,
111 Settings.Gservices.MEMCHECK_SYSTEM_SOFT_THRESHOLD,
112 MEMCHECK_DEFAULT_SYSTEM_SOFT_THRESHOLD,
113 Settings.Gservices.MEMCHECK_SYSTEM_HARD_THRESHOLD,
114 MEMCHECK_DEFAULT_SYSTEM_HARD_THRESHOLD);
115 final MemMonitor mPhoneMemMonitor = new MemMonitor("com.android.phone",
116 Settings.Gservices.MEMCHECK_PHONE_ENABLED,
117 Settings.Gservices.MEMCHECK_PHONE_SOFT_THRESHOLD,
118 MEMCHECK_DEFAULT_PHONE_SOFT_THRESHOLD,
119 Settings.Gservices.MEMCHECK_PHONE_HARD_THRESHOLD,
120 MEMCHECK_DEFAULT_PHONE_HARD_THRESHOLD);
121
122 final Calendar mCalendar = Calendar.getInstance();
123 long mMemcheckLastTime;
124 long mMemcheckExecStartTime;
125 long mMemcheckExecEndTime;
126 int mMinScreenOff = MEMCHECK_DEFAULT_MIN_SCREEN_OFF;
127 int mMinAlarm = MEMCHECK_DEFAULT_MIN_ALARM;
128 boolean mNeedScheduledCheck;
129 PendingIntent mCheckupIntent;
130 PendingIntent mRebootIntent;
131
132 long mBootTime;
133 int mRebootInterval;
134
135 boolean mReqRebootNoWait; // should wait for one interval before reboot?
136 int mReqRebootInterval = -1; // >= 0 if a reboot has been requested
137 int mReqRebootStartTime = -1; // >= 0 if a specific start time has been requested
138 int mReqRebootWindow = -1; // >= 0 if a specific window has been requested
139 int mReqMinScreenOff = -1; // >= 0 if a specific screen off time has been requested
140 int mReqMinNextAlarm = -1; // >= 0 if specific time to next alarm has been requested
141 int mReqRecheckInterval= -1; // >= 0 if a specific recheck interval has been requested
142
143 /**
144 * This class monitors the memory in a particular process.
145 */
146 final class MemMonitor {
147 final String mProcessName;
148 final String mEnabledSetting;
149 final String mSoftSetting;
150 final String mHardSetting;
151
152 int mSoftThreshold;
153 int mHardThreshold;
154 boolean mEnabled;
155 long mLastPss;
156
157 static final int STATE_OK = 0;
158 static final int STATE_SOFT = 1;
159 static final int STATE_HARD = 2;
160 int mState;
161
162 MemMonitor(String processName, String enabledSetting,
163 String softSetting, int defSoftThreshold,
164 String hardSetting, int defHardThreshold) {
165 mProcessName = processName;
166 mEnabledSetting = enabledSetting;
167 mSoftSetting = softSetting;
168 mHardSetting = hardSetting;
169 mSoftThreshold = defSoftThreshold;
170 mHardThreshold = defHardThreshold;
171 }
172
173 void retrieveSettings(ContentResolver resolver) {
174 mSoftThreshold = Settings.Gservices.getInt(
175 resolver, mSoftSetting, mSoftThreshold);
176 mHardThreshold = Settings.Gservices.getInt(
177 resolver, mHardSetting, mHardThreshold);
178 mEnabled = Settings.Gservices.getInt(
179 resolver, mEnabledSetting, 0) != 0;
180 }
181
182 boolean checkLocked(long curTime, int pid, int pss) {
183 mLastPss = pss;
184 if (mLastPss < mSoftThreshold) {
185 mState = STATE_OK;
186 } else if (mLastPss < mHardThreshold) {
187 mState = STATE_SOFT;
188 } else {
189 mState = STATE_HARD;
190 }
191 EventLog.writeEvent(EVENT_LOG_PROC_PSS_TAG, mProcessName, pid, mLastPss);
192
193 if (mState == STATE_OK) {
194 // Memory is good, don't recover.
195 return false;
196 }
197
198 if (mState == STATE_HARD) {
199 // Memory is really bad, kill right now.
200 EventLog.writeEvent(EVENT_LOG_HARD_RESET_TAG, mProcessName, pid,
201 mHardThreshold, mLastPss);
202 return mEnabled;
203 }
204
205 // It is time to schedule a reset...
206 // Check if we are currently within the time to kill processes due
207 // to memory use.
208 computeMemcheckTimesLocked(curTime);
209 String skipReason = null;
210 if (curTime < mMemcheckExecStartTime || curTime > mMemcheckExecEndTime) {
211 skipReason = "time";
212 } else {
213 skipReason = shouldWeBeBrutalLocked(curTime);
214 }
215 EventLog.writeEvent(EVENT_LOG_SOFT_RESET_TAG, mProcessName, pid,
216 mSoftThreshold, mLastPss, skipReason != null ? skipReason : "");
217 if (skipReason != null) {
218 mNeedScheduledCheck = true;
219 return false;
220 }
221 return mEnabled;
222 }
223
224 void clear() {
225 mLastPss = 0;
226 mState = STATE_OK;
227 }
228 }
229
230 /**
231 * Used for scheduling monitor callbacks and checking memory usage.
232 */
233 final class HeartbeatHandler extends Handler {
234 @Override
235 public void handleMessage(Message msg) {
236 switch (msg.what) {
237 case GLOBAL_PSS: {
238 if (mHaveGlobalPss) {
239 // During the last pass we collected pss information, so
240 // now it is time to report it.
241 mHaveGlobalPss = false;
242 if (localLOGV) Log.v(TAG, "Received global pss, logging.");
243 logGlobalMemory();
244 }
245 } break;
246
247 case MONITOR: {
248 if (mHavePss) {
249 // During the last pass we collected pss information, so
250 // now it is time to report it.
251 mHavePss = false;
252 if (localLOGV) Log.v(TAG, "Have pss, checking memory.");
253 checkMemory();
254 }
255
256 if (mHaveGlobalPss) {
257 // During the last pass we collected pss information, so
258 // now it is time to report it.
259 mHaveGlobalPss = false;
260 if (localLOGV) Log.v(TAG, "Have global pss, logging.");
261 logGlobalMemory();
262 }
263
264 long now = SystemClock.uptimeMillis();
265
266 // See if we should force a reboot.
267 int rebootInterval = mReqRebootInterval >= 0
268 ? mReqRebootInterval : Settings.Gservices.getInt(
269 mResolver, Settings.Gservices.REBOOT_INTERVAL,
270 REBOOT_DEFAULT_INTERVAL);
271 if (mRebootInterval != rebootInterval) {
272 mRebootInterval = rebootInterval;
273 // We have been running long enough that a reboot can
274 // be considered...
275 checkReboot(false);
276 }
277
278 // See if we should check memory conditions.
279 long memCheckInterval = Settings.Gservices.getLong(
280 mResolver, Settings.Gservices.MEMCHECK_INTERVAL,
281 MEMCHECK_DEFAULT_INTERVAL) * 1000;
282 if ((mLastMemCheckTime+memCheckInterval) < now) {
283 // It is now time to collect pss information. This
284 // is async so we won't report it now. And to keep
285 // things simple, we will assume that everyone has
286 // reported back by the next MONITOR message.
287 mLastMemCheckTime = now;
288 if (localLOGV) Log.v(TAG, "Collecting memory usage.");
289 collectMemory();
290 mHavePss = true;
291
292 long memCheckRealtimeInterval = Settings.Gservices.getLong(
293 mResolver, Settings.Gservices.MEMCHECK_LOG_REALTIME_INTERVAL,
294 MEMCHECK_DEFAULT_LOG_REALTIME_INTERVAL) * 1000;
295 long realtimeNow = SystemClock.elapsedRealtime();
296 if ((mLastMemCheckRealtime+memCheckRealtimeInterval) < realtimeNow) {
297 mLastMemCheckRealtime = realtimeNow;
298 if (localLOGV) Log.v(TAG, "Collecting global memory usage.");
299 collectGlobalMemory();
300 mHaveGlobalPss = true;
301 }
302 }
303
304 final int size = mMonitors.size();
305 for (int i = 0 ; i < size ; i++) {
306 mCurrentMonitor = mMonitors.get(i);
307 mCurrentMonitor.monitor();
308 }
309
310 synchronized (Watchdog.this) {
311 mCompleted = true;
312 mCurrentMonitor = null;
313 }
314 } break;
315 }
316 }
317 }
318
319 final class GlobalPssCollected implements Runnable {
320 public void run() {
321 mHandler.sendEmptyMessage(GLOBAL_PSS);
322 }
323 }
324
325 final class CheckupReceiver extends BroadcastReceiver {
326 @Override
327 public void onReceive(Context c, Intent intent) {
328 if (localLOGV) Log.v(TAG, "Alarm went off, checking memory.");
329 checkMemory();
330 }
331 }
332
333 final class RebootReceiver extends BroadcastReceiver {
334 @Override
335 public void onReceive(Context c, Intent intent) {
336 if (localLOGV) Log.v(TAG, "Alarm went off, checking reboot.");
337 checkReboot(true);
338 }
339 }
340
341 final class RebootRequestReceiver extends BroadcastReceiver {
342 @Override
343 public void onReceive(Context c, Intent intent) {
344 mReqRebootNoWait = intent.getIntExtra("nowait", 0) != 0;
345 mReqRebootInterval = intent.getIntExtra("interval", -1);
346 mReqRebootStartTime = intent.getIntExtra("startTime", -1);
347 mReqRebootWindow = intent.getIntExtra("window", -1);
348 mReqMinScreenOff = intent.getIntExtra("minScreenOff", -1);
349 mReqMinNextAlarm = intent.getIntExtra("minNextAlarm", -1);
350 mReqRecheckInterval = intent.getIntExtra("recheckInterval", -1);
351 EventLog.writeEvent(EVENT_LOG_REQUESTED_REBOOT_TAG,
352 mReqRebootNoWait ? 1 : 0, mReqRebootInterval,
353 mReqRecheckInterval, mReqRebootStartTime,
354 mReqRebootWindow, mReqMinScreenOff, mReqMinNextAlarm);
355 checkReboot(true);
356 }
357 }
358
359 public interface Monitor {
360 void monitor();
361 }
362
363 public interface PssRequestor {
364 void requestPss();
365 }
366
367 public class PssStats {
368 public int mEmptyPss;
369 public int mEmptyCount;
370 public int mBackgroundPss;
371 public int mBackgroundCount;
372 public int mServicePss;
373 public int mServiceCount;
374 public int mVisiblePss;
375 public int mVisibleCount;
376 public int mForegroundPss;
377 public int mForegroundCount;
378
379 public int mNoPssCount;
380
381 public int mProcDeaths[] = new int[10];
382 }
383
384 public static Watchdog getInstance() {
385 if (sWatchdog == null) {
386 sWatchdog = new Watchdog();
387 }
388
389 return sWatchdog;
390 }
391
392 private Watchdog() {
393 super("watchdog");
394 mHandler = new HeartbeatHandler();
395 mGlobalPssCollected = new GlobalPssCollected();
396 }
397
398 public void init(Context context, BatteryService battery,
399 PowerManagerService power, AlarmManagerService alarm,
400 ActivityManagerService activity) {
401 mResolver = context.getContentResolver();
402 mBattery = battery;
403 mPower = power;
404 mAlarm = alarm;
405 mActivity = activity;
406
407 context.registerReceiver(new CheckupReceiver(),
408 new IntentFilter(CHECKUP_ACTION));
409 mCheckupIntent = PendingIntent.getBroadcast(context,
410 0, new Intent(CHECKUP_ACTION), 0);
411
412 context.registerReceiver(new RebootReceiver(),
413 new IntentFilter(REBOOT_ACTION));
414 mRebootIntent = PendingIntent.getBroadcast(context,
415 0, new Intent(REBOOT_ACTION), 0);
416
417 context.registerReceiver(new RebootRequestReceiver(),
418 new IntentFilter(Intent.ACTION_REBOOT),
419 android.Manifest.permission.REBOOT, null);
420
421 mBootTime = System.currentTimeMillis();
422 }
423
424 public void processStarted(PssRequestor req, String name, int pid) {
425 synchronized (this) {
426 if ("com.android.phone".equals(name)) {
427 mPhoneReq = req;
428 mPhonePid = pid;
429 mPhonePss = 0;
430 }
431 }
432 }
433
434 public void reportPss(PssRequestor req, String name, int pss) {
435 synchronized (this) {
436 if (mPhoneReq == req) {
437 mPhonePss = pss;
438 }
439 }
440 }
441
442 public void addMonitor(Monitor monitor) {
443 synchronized (this) {
444 if (isAlive()) {
445 throw new RuntimeException("Monitors can't be added while the Watchdog is running");
446 }
447 mMonitors.add(monitor);
448 }
449 }
450
451 /**
452 * Retrieve memory usage information from specific processes being
453 * monitored. This is an async operation, so must be done before doing
454 * memory checks.
455 */
456 void collectMemory() {
457 synchronized (this) {
458 if (mPhoneReq != null) {
459 mPhoneReq.requestPss();
460 }
461 }
462 }
463
464 /**
465 * Retrieve memory usage over all application processes. This is an
466 * async operation, so must be done before doing memory checks.
467 */
468 void collectGlobalMemory() {
469 mActivity.requestPss(mGlobalPssCollected);
470 }
471
472 /**
473 * Check memory usage in the system, scheduling kills/reboots as needed.
474 * This always runs on the mHandler thread.
475 */
476 void checkMemory() {
477 boolean needScheduledCheck;
478 long curTime;
479 long nextTime = 0;
480
481 long recheckInterval = Settings.Gservices.getLong(
482 mResolver, Settings.Gservices.MEMCHECK_RECHECK_INTERVAL,
483 MEMCHECK_DEFAULT_RECHECK_INTERVAL) * 1000;
484
485 mSystemMemMonitor.retrieveSettings(mResolver);
486 mPhoneMemMonitor.retrieveSettings(mResolver);
487 retrieveBrutalityAmount();
488
489 synchronized (this) {
490 curTime = System.currentTimeMillis();
491 mNeedScheduledCheck = false;
492
493 // How is the system doing?
494 if (mSystemMemMonitor.checkLocked(curTime, Process.myPid(),
495 (int)Process.getPss(Process.myPid()))) {
496 // Not good! Time to suicide.
497 mForceKillSystem = true;
498 notifyAll();
499 return;
500 }
501
502 // How is the phone process doing?
503 if (mPhoneReq != null) {
504 if (mPhoneMemMonitor.checkLocked(curTime, mPhonePid,
505 mPhonePss)) {
506 // Just kill the phone process and let it restart.
Dianne Hackborn723738c2009-06-25 19:48:04 -0700507 Log.i(TAG, "Watchdog is killing the phone process");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 Process.killProcess(mPhonePid);
509 }
510 } else {
511 mPhoneMemMonitor.clear();
512 }
513
514 needScheduledCheck = mNeedScheduledCheck;
515 if (needScheduledCheck) {
516 // Something is going bad, but now is not a good time to
517 // tear things down... schedule an alarm to check again soon.
518 nextTime = curTime + recheckInterval;
519 if (nextTime < mMemcheckExecStartTime) {
520 nextTime = mMemcheckExecStartTime;
521 } else if (nextTime >= mMemcheckExecEndTime){
522 // Need to check during next exec time... so that needs
523 // to be computed.
524 if (localLOGV) Log.v(TAG, "Computing next time range");
525 computeMemcheckTimesLocked(nextTime);
526 nextTime = mMemcheckExecStartTime;
527 }
528
529 if (localLOGV) {
530 mCalendar.setTimeInMillis(nextTime);
531 Log.v(TAG, "Next Alarm Time: " + mCalendar);
532 }
533 }
534 }
535
536 if (needScheduledCheck) {
537 if (localLOGV) Log.v(TAG, "Scheduling next memcheck alarm for "
538 + ((nextTime-curTime)/1000/60) + "m from now");
539 mAlarm.remove(mCheckupIntent);
540 mAlarm.set(AlarmManager.RTC_WAKEUP, nextTime, mCheckupIntent);
541 } else {
542 if (localLOGV) Log.v(TAG, "No need to schedule a memcheck alarm!");
543 mAlarm.remove(mCheckupIntent);
544 }
545 }
546
547 final PssStats mPssStats = new PssStats();
548 final String[] mMemInfoFields = new String[] {
549 "MemFree:", "Buffers:", "Cached:",
550 "Active:", "Inactive:",
551 "AnonPages:", "Mapped:", "Slab:",
552 "SReclaimable:", "SUnreclaim:", "PageTables:" };
553 final long[] mMemInfoSizes = new long[mMemInfoFields.length];
554 final String[] mVMStatFields = new String[] {
555 "pgfree ", "pgactivate ", "pgdeactivate ",
556 "pgfault ", "pgmajfault " };
557 final long[] mVMStatSizes = new long[mVMStatFields.length];
558 final long[] mPrevVMStatSizes = new long[mVMStatFields.length];
559 long mLastLogGlobalMemoryTime;
560
561 void logGlobalMemory() {
562 PssStats stats = mPssStats;
563 mActivity.collectPss(stats);
564 EventLog.writeEvent(EVENT_LOG_PSS_STATS_TAG,
565 stats.mEmptyPss, stats.mEmptyCount,
566 stats.mBackgroundPss, stats.mBackgroundCount,
567 stats.mServicePss, stats.mServiceCount,
568 stats.mVisiblePss, stats.mVisibleCount,
569 stats.mForegroundPss, stats.mForegroundCount,
570 stats.mNoPssCount);
571 EventLog.writeEvent(EVENT_LOG_PROC_STATS_TAG,
572 stats.mProcDeaths[0], stats.mProcDeaths[1], stats.mProcDeaths[2],
573 stats.mProcDeaths[3], stats.mProcDeaths[4]);
574 Process.readProcLines("/proc/meminfo", mMemInfoFields, mMemInfoSizes);
575 for (int i=0; i<mMemInfoSizes.length; i++) {
576 mMemInfoSizes[i] *= 1024;
577 }
578 EventLog.writeEvent(EVENT_LOG_MEMINFO_TAG,
579 (int)mMemInfoSizes[0], (int)mMemInfoSizes[1], (int)mMemInfoSizes[2],
580 (int)mMemInfoSizes[3], (int)mMemInfoSizes[4],
581 (int)mMemInfoSizes[5], (int)mMemInfoSizes[6], (int)mMemInfoSizes[7],
582 (int)mMemInfoSizes[8], (int)mMemInfoSizes[9], (int)mMemInfoSizes[10]);
583 long now = SystemClock.uptimeMillis();
584 long dur = now - mLastLogGlobalMemoryTime;
585 mLastLogGlobalMemoryTime = now;
586 Process.readProcLines("/proc/vmstat", mVMStatFields, mVMStatSizes);
587 for (int i=0; i<mVMStatSizes.length; i++) {
588 long v = mVMStatSizes[i];
589 mVMStatSizes[i] -= mPrevVMStatSizes[i];
590 mPrevVMStatSizes[i] = v;
591 }
592 EventLog.writeEvent(EVENT_LOG_VMSTAT_TAG, dur,
593 (int)mVMStatSizes[0], (int)mVMStatSizes[1], (int)mVMStatSizes[2],
594 (int)mVMStatSizes[3], (int)mVMStatSizes[4]);
595 }
596
597 void checkReboot(boolean fromAlarm) {
598 int rebootInterval = mReqRebootInterval >= 0 ? mReqRebootInterval
599 : Settings.Gservices.getInt(
600 mResolver, Settings.Gservices.REBOOT_INTERVAL,
601 REBOOT_DEFAULT_INTERVAL);
602 mRebootInterval = rebootInterval;
603 if (rebootInterval <= 0) {
604 // No reboot interval requested.
605 if (localLOGV) Log.v(TAG, "No need to schedule a reboot alarm!");
606 mAlarm.remove(mRebootIntent);
607 return;
608 }
609
610 long rebootStartTime = mReqRebootStartTime >= 0 ? mReqRebootStartTime
611 : Settings.Gservices.getLong(
612 mResolver, Settings.Gservices.REBOOT_START_TIME,
613 REBOOT_DEFAULT_START_TIME);
614 long rebootWindowMillis = (mReqRebootWindow >= 0 ? mReqRebootWindow
615 : Settings.Gservices.getLong(
616 mResolver, Settings.Gservices.REBOOT_WINDOW,
617 REBOOT_DEFAULT_WINDOW)) * 1000;
618 long recheckInterval = (mReqRecheckInterval >= 0 ? mReqRecheckInterval
619 : Settings.Gservices.getLong(
620 mResolver, Settings.Gservices.MEMCHECK_RECHECK_INTERVAL,
621 MEMCHECK_DEFAULT_RECHECK_INTERVAL)) * 1000;
622
623 retrieveBrutalityAmount();
624
625 long realStartTime;
626 long now;
627
628 synchronized (this) {
629 now = System.currentTimeMillis();
630 realStartTime = computeCalendarTime(mCalendar, now,
631 rebootStartTime);
632
633 long rebootIntervalMillis = rebootInterval*24*60*60*1000;
634 if (DB || mReqRebootNoWait ||
635 (now-mBootTime) >= (rebootIntervalMillis-rebootWindowMillis)) {
636 if (fromAlarm && rebootWindowMillis <= 0) {
637 // No reboot window -- just immediately reboot.
638 EventLog.writeEvent(EVENT_LOG_SCHEDULED_REBOOT_TAG, now,
639 (int)rebootIntervalMillis, (int)rebootStartTime*1000,
640 (int)rebootWindowMillis, "");
641 rebootSystem("Checkin scheduled forced");
642 return;
643 }
644
645 // Are we within the reboot window?
646 if (now < realStartTime) {
647 // Schedule alarm for next check interval.
648 realStartTime = computeCalendarTime(mCalendar,
649 now, rebootStartTime);
650 } else if (now < (realStartTime+rebootWindowMillis)) {
651 String doit = shouldWeBeBrutalLocked(now);
652 EventLog.writeEvent(EVENT_LOG_SCHEDULED_REBOOT_TAG, now,
653 (int)rebootInterval, (int)rebootStartTime*1000,
654 (int)rebootWindowMillis, doit != null ? doit : "");
655 if (doit == null) {
656 rebootSystem("Checked scheduled range");
657 return;
658 }
659
660 // Schedule next alarm either within the window or in the
661 // next interval.
662 if ((now+recheckInterval) >= (realStartTime+rebootWindowMillis)) {
663 realStartTime = computeCalendarTime(mCalendar,
664 now + rebootIntervalMillis, rebootStartTime);
665 } else {
666 realStartTime = now + recheckInterval;
667 }
668 } else {
669 // Schedule alarm for next check interval.
670 realStartTime = computeCalendarTime(mCalendar,
671 now + rebootIntervalMillis, rebootStartTime);
672 }
673 }
674 }
675
676 if (localLOGV) Log.v(TAG, "Scheduling next reboot alarm for "
677 + ((realStartTime-now)/1000/60) + "m from now");
678 mAlarm.remove(mRebootIntent);
679 mAlarm.set(AlarmManager.RTC_WAKEUP, realStartTime, mRebootIntent);
680 }
681
682 /**
683 * Perform a full reboot of the system.
684 */
685 void rebootSystem(String reason) {
686 Log.i(TAG, "Rebooting system because: " + reason);
687 try {
688 android.os.Power.reboot(reason);
689 } catch (IOException e) {
690 Log.e(TAG, "Reboot failed!", e);
691 }
692 }
693
694 /**
695 * Load the current Gservices settings for when
696 * {@link #shouldWeBeBrutalLocked} will allow the brutality to happen.
697 * Must not be called with the lock held.
698 */
699 void retrieveBrutalityAmount() {
700 mMinScreenOff = (mReqMinScreenOff >= 0 ? mReqMinScreenOff
701 : Settings.Gservices.getInt(
702 mResolver, Settings.Gservices.MEMCHECK_MIN_SCREEN_OFF,
703 MEMCHECK_DEFAULT_MIN_SCREEN_OFF)) * 1000;
704 mMinAlarm = (mReqMinNextAlarm >= 0 ? mReqMinNextAlarm
705 : Settings.Gservices.getInt(
706 mResolver, Settings.Gservices.MEMCHECK_MIN_ALARM,
707 MEMCHECK_DEFAULT_MIN_ALARM)) * 1000;
708 }
709
710 /**
711 * Determine whether it is a good time to kill, crash, or otherwise
712 * plunder the current situation for the overall long-term benefit of
713 * the world.
714 *
715 * @param curTime The current system time.
716 * @return Returns null if this is a good time, else a String with the
717 * text of why it is not a good time.
718 */
719 String shouldWeBeBrutalLocked(long curTime) {
720 if (mBattery == null || !mBattery.isPowered()) {
721 return "battery";
722 }
723
724 if (mMinScreenOff >= 0 && (mPower == null ||
725 mPower.timeSinceScreenOn() < mMinScreenOff)) {
726 return "screen";
727 }
728
729 if (mMinAlarm >= 0 && (mAlarm == null ||
730 mAlarm.timeToNextAlarm() < mMinAlarm)) {
731 return "alarm";
732 }
733
734 return null;
735 }
736
737 /**
738 * Compute the times during which we next would like to perform process
739 * restarts.
740 *
741 * @param curTime The current system time.
742 */
743 void computeMemcheckTimesLocked(long curTime) {
744 if (mMemcheckLastTime == curTime) {
745 return;
746 }
747
748 mMemcheckLastTime = curTime;
749
750 long memcheckExecStartTime = Settings.Gservices.getLong(
751 mResolver, Settings.Gservices.MEMCHECK_EXEC_START_TIME,
752 MEMCHECK_DEFAULT_EXEC_START_TIME);
753 long memcheckExecEndTime = Settings.Gservices.getLong(
754 mResolver, Settings.Gservices.MEMCHECK_EXEC_END_TIME,
755 MEMCHECK_DEFAULT_EXEC_END_TIME);
756
757 mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime,
758 memcheckExecEndTime);
759 if (mMemcheckExecEndTime < curTime) {
760 memcheckExecStartTime += 24*60*60;
761 memcheckExecEndTime += 24*60*60;
762 mMemcheckExecEndTime = computeCalendarTime(mCalendar, curTime,
763 memcheckExecEndTime);
764 }
765 mMemcheckExecStartTime = computeCalendarTime(mCalendar, curTime,
766 memcheckExecStartTime);
767
768 if (localLOGV) {
769 mCalendar.setTimeInMillis(curTime);
770 Log.v(TAG, "Current Time: " + mCalendar);
771 mCalendar.setTimeInMillis(mMemcheckExecStartTime);
772 Log.v(TAG, "Start Check Time: " + mCalendar);
773 mCalendar.setTimeInMillis(mMemcheckExecEndTime);
774 Log.v(TAG, "End Check Time: " + mCalendar);
775 }
776 }
777
778 static long computeCalendarTime(Calendar c, long curTime,
779 long secondsSinceMidnight) {
780
781 // start with now
782 c.setTimeInMillis(curTime);
783
784 int val = (int)secondsSinceMidnight / (60*60);
785 c.set(Calendar.HOUR_OF_DAY, val);
786 secondsSinceMidnight -= val * (60*60);
787 val = (int)secondsSinceMidnight / 60;
788 c.set(Calendar.MINUTE, val);
789 c.set(Calendar.SECOND, (int)secondsSinceMidnight - (val*60));
790 c.set(Calendar.MILLISECOND, 0);
791
792 long newTime = c.getTimeInMillis();
793 if (newTime < curTime) {
794 // The given time (in seconds since midnight) has already passed for today, so advance
795 // by one day (due to daylight savings, etc., the delta may differ from 24 hours).
796 c.add(Calendar.DAY_OF_MONTH, 1);
797 newTime = c.getTimeInMillis();
798 }
799
800 return newTime;
801 }
802
803 @Override
804 public void run() {
805 while (true) {
806 mCompleted = false;
807 mHandler.sendEmptyMessage(MONITOR);
808
809 synchronized (this) {
810 long timeout = TIME_TO_WAIT;
811
812 // NOTE: We use uptimeMillis() here because we do not want to increment the time we
813 // wait while asleep. If the device is asleep then the thing that we are waiting
814 // to timeout on is asleep as well and won't have a chance to run. Causing a false
815 // positive on when to kill things.
816 long start = SystemClock.uptimeMillis();
817 do {
818 try {
819 wait(timeout);
820 } catch (InterruptedException e) {
821 if (SystemProperties.getBoolean("ro.secure", false)) {
822 // If this is a secure build, just log the error.
823 Log.e("WatchDog", "Woof! Woof! Interrupter!");
824 } else {
825 throw new AssertionError("Someone interrupted the watchdog");
826 }
827 }
828 timeout = TIME_TO_WAIT - (SystemClock.uptimeMillis() - start);
829 } while (timeout > 0 && !mForceKillSystem);
830
831 if (mCompleted && !mForceKillSystem) {
832 // The monitors have returned.
833 continue;
834 }
835 }
836
837 // If we got here, that means that the system is most likely hung.
838 // First send a SIGQUIT so that we can see where it was hung. Then
839 // kill this process so that the system will restart.
840 String name = (mCurrentMonitor != null) ? mCurrentMonitor.getClass().getName() : "null";
841 EventLog.writeEvent(EVENT_LOG_TAG, name);
842 Process.sendSignal(Process.myPid(), Process.SIGNAL_QUIT);
843
844 // Wait a bit longer before killing so we can make sure that the stacks are captured.
845 try {
846 Thread.sleep(10*1000);
847 } catch (InterruptedException e) {
848 }
849
850 // Only kill the process if the debugger is not attached.
851 if (!Debug.isDebuggerConnected()) {
Dianne Hackborn723738c2009-06-25 19:48:04 -0700852 Log.i(TAG, "Watchdog is killing the system process");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 Process.killProcess(Process.myPid());
854 }
855 }
856 }
857}