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