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.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.
507                    Log.i(TAG, "Watchdog is killing the phone process");
508                    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()) {
852                Log.i(TAG, "Watchdog is killing the system process");
853                Process.killProcess(Process.myPid());
854            }
855        }
856    }
857}
858