2009-03-03 19:31:44 -08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package com.android.server;
|
|
|
|
|
|
|
|
import com.android.server.am.ActivityManagerService;
|
2012-06-18 18:29:13 -07:00
|
|
|
import com.android.server.power.PowerManagerService;
|
2009-03-03 19:31:44 -08:00
|
|
|
|
|
|
|
import android.app.AlarmManager;
|
|
|
|
import android.app.PendingIntent;
|
|
|
|
import android.content.BroadcastReceiver;
|
|
|
|
import android.content.ContentResolver;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.IntentFilter;
|
2012-10-02 19:11:19 -07:00
|
|
|
import android.os.BatteryManager;
|
2009-03-03 19:31:44 -08:00
|
|
|
import android.os.Debug;
|
|
|
|
import android.os.Handler;
|
|
|
|
import android.os.Message;
|
|
|
|
import android.os.Process;
|
2010-03-08 14:48:40 -08:00
|
|
|
import android.os.ServiceManager;
|
2009-03-03 19:31:44 -08:00
|
|
|
import android.os.SystemClock;
|
|
|
|
import android.os.SystemProperties;
|
|
|
|
import android.util.EventLog;
|
2010-03-04 14:20:31 -08:00
|
|
|
import android.util.Log;
|
2010-02-26 18:56:32 -08:00
|
|
|
import android.util.Slog;
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2010-03-04 14:20:31 -08:00
|
|
|
import java.io.File;
|
2012-11-29 11:42:11 -08:00
|
|
|
import java.io.FileWriter;
|
|
|
|
import java.io.IOException;
|
2009-03-03 19:31:44 -08:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Calendar;
|
|
|
|
|
|
|
|
/** This class calls its monitor every minute. Killing this process if they don't return **/
|
|
|
|
public class Watchdog extends Thread {
|
|
|
|
static final String TAG = "Watchdog";
|
2011-04-06 19:22:23 -07:00
|
|
|
static final boolean localLOGV = false || false;
|
2009-03-03 19:31:44 -08:00
|
|
|
|
|
|
|
// Set this to true to use debug default values.
|
|
|
|
static final boolean DB = false;
|
|
|
|
|
2010-06-04 14:55:02 -07:00
|
|
|
// Set this to true to have the watchdog record kernel thread stacks when it fires
|
|
|
|
static final boolean RECORD_KERNEL_THREADS = true;
|
|
|
|
|
2009-03-03 19:31:44 -08:00
|
|
|
static final int MONITOR = 2718;
|
|
|
|
|
2011-08-25 17:12:37 -07:00
|
|
|
static final int TIME_TO_RESTART = DB ? 15*1000 : 60*1000;
|
2010-05-28 12:01:56 -07:00
|
|
|
static final int TIME_TO_WAIT = TIME_TO_RESTART / 2;
|
2009-03-03 19:31:44 -08:00
|
|
|
|
|
|
|
static final int MEMCHECK_DEFAULT_MIN_SCREEN_OFF = DB ? 1*60 : 5*60; // 5 minutes
|
|
|
|
static final int MEMCHECK_DEFAULT_MIN_ALARM = DB ? 1*60 : 3*60; // 3 minutes
|
|
|
|
static final int MEMCHECK_DEFAULT_RECHECK_INTERVAL = DB ? 1*60 : 5*60; // 5 minutes
|
|
|
|
|
|
|
|
static final int REBOOT_DEFAULT_INTERVAL = DB ? 1 : 0; // never force reboot
|
|
|
|
static final int REBOOT_DEFAULT_START_TIME = 3*60*60; // 3:00am
|
|
|
|
static final int REBOOT_DEFAULT_WINDOW = 60*60; // within 1 hour
|
|
|
|
|
|
|
|
static final String REBOOT_ACTION = "com.android.service.Watchdog.REBOOT";
|
|
|
|
|
2012-06-08 17:23:59 -07:00
|
|
|
static final String[] NATIVE_STACKS_OF_INTEREST = new String[] {
|
|
|
|
"/system/bin/mediaserver",
|
|
|
|
"/system/bin/sdcard",
|
|
|
|
"/system/bin/surfaceflinger"
|
|
|
|
};
|
|
|
|
|
2009-03-03 19:31:44 -08:00
|
|
|
static Watchdog sWatchdog;
|
|
|
|
|
|
|
|
/* This handler will be used to post message back onto the main thread */
|
|
|
|
final Handler mHandler;
|
|
|
|
final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
|
|
|
|
ContentResolver mResolver;
|
|
|
|
BatteryService mBattery;
|
|
|
|
PowerManagerService mPower;
|
|
|
|
AlarmManagerService mAlarm;
|
|
|
|
ActivityManagerService mActivity;
|
|
|
|
boolean mCompleted;
|
|
|
|
boolean mForceKillSystem;
|
|
|
|
Monitor mCurrentMonitor;
|
|
|
|
|
|
|
|
int mPhonePid;
|
|
|
|
|
|
|
|
final Calendar mCalendar = Calendar.getInstance();
|
|
|
|
int mMinScreenOff = MEMCHECK_DEFAULT_MIN_SCREEN_OFF;
|
|
|
|
int mMinAlarm = MEMCHECK_DEFAULT_MIN_ALARM;
|
|
|
|
boolean mNeedScheduledCheck;
|
|
|
|
PendingIntent mCheckupIntent;
|
|
|
|
PendingIntent mRebootIntent;
|
|
|
|
|
|
|
|
long mBootTime;
|
|
|
|
int mRebootInterval;
|
|
|
|
|
|
|
|
boolean mReqRebootNoWait; // should wait for one interval before reboot?
|
|
|
|
int mReqRebootInterval = -1; // >= 0 if a reboot has been requested
|
|
|
|
int mReqRebootStartTime = -1; // >= 0 if a specific start time has been requested
|
|
|
|
int mReqRebootWindow = -1; // >= 0 if a specific window has been requested
|
|
|
|
int mReqMinScreenOff = -1; // >= 0 if a specific screen off time has been requested
|
|
|
|
int mReqMinNextAlarm = -1; // >= 0 if specific time to next alarm has been requested
|
|
|
|
int mReqRecheckInterval= -1; // >= 0 if a specific recheck interval has been requested
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Used for scheduling monitor callbacks and checking memory usage.
|
|
|
|
*/
|
|
|
|
final class HeartbeatHandler extends Handler {
|
|
|
|
@Override
|
|
|
|
public void handleMessage(Message msg) {
|
|
|
|
switch (msg.what) {
|
|
|
|
case MONITOR: {
|
|
|
|
// See if we should force a reboot.
|
|
|
|
int rebootInterval = mReqRebootInterval >= 0
|
2012-09-26 17:58:19 -07:00
|
|
|
? mReqRebootInterval : REBOOT_DEFAULT_INTERVAL;
|
2009-03-03 19:31:44 -08:00
|
|
|
if (mRebootInterval != rebootInterval) {
|
|
|
|
mRebootInterval = rebootInterval;
|
|
|
|
// We have been running long enough that a reboot can
|
|
|
|
// be considered...
|
|
|
|
checkReboot(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
final int size = mMonitors.size();
|
|
|
|
for (int i = 0 ; i < size ; i++) {
|
|
|
|
mCurrentMonitor = mMonitors.get(i);
|
|
|
|
mCurrentMonitor.monitor();
|
|
|
|
}
|
|
|
|
|
|
|
|
synchronized (Watchdog.this) {
|
|
|
|
mCompleted = true;
|
|
|
|
mCurrentMonitor = null;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
final class RebootReceiver extends BroadcastReceiver {
|
|
|
|
@Override
|
|
|
|
public void onReceive(Context c, Intent intent) {
|
2010-02-26 18:56:32 -08:00
|
|
|
if (localLOGV) Slog.v(TAG, "Alarm went off, checking reboot.");
|
2009-03-03 19:31:44 -08:00
|
|
|
checkReboot(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
final class RebootRequestReceiver extends BroadcastReceiver {
|
|
|
|
@Override
|
|
|
|
public void onReceive(Context c, Intent intent) {
|
|
|
|
mReqRebootNoWait = intent.getIntExtra("nowait", 0) != 0;
|
|
|
|
mReqRebootInterval = intent.getIntExtra("interval", -1);
|
|
|
|
mReqRebootStartTime = intent.getIntExtra("startTime", -1);
|
|
|
|
mReqRebootWindow = intent.getIntExtra("window", -1);
|
|
|
|
mReqMinScreenOff = intent.getIntExtra("minScreenOff", -1);
|
|
|
|
mReqMinNextAlarm = intent.getIntExtra("minNextAlarm", -1);
|
|
|
|
mReqRecheckInterval = intent.getIntExtra("recheckInterval", -1);
|
2009-12-04 10:31:43 -08:00
|
|
|
EventLog.writeEvent(EventLogTags.WATCHDOG_REQUESTED_REBOOT,
|
2009-03-03 19:31:44 -08:00
|
|
|
mReqRebootNoWait ? 1 : 0, mReqRebootInterval,
|
|
|
|
mReqRecheckInterval, mReqRebootStartTime,
|
|
|
|
mReqRebootWindow, mReqMinScreenOff, mReqMinNextAlarm);
|
|
|
|
checkReboot(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public interface Monitor {
|
|
|
|
void monitor();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Watchdog getInstance() {
|
|
|
|
if (sWatchdog == null) {
|
|
|
|
sWatchdog = new Watchdog();
|
|
|
|
}
|
|
|
|
|
|
|
|
return sWatchdog;
|
|
|
|
}
|
|
|
|
|
|
|
|
private Watchdog() {
|
|
|
|
super("watchdog");
|
|
|
|
mHandler = new HeartbeatHandler();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void init(Context context, BatteryService battery,
|
|
|
|
PowerManagerService power, AlarmManagerService alarm,
|
|
|
|
ActivityManagerService activity) {
|
|
|
|
mResolver = context.getContentResolver();
|
|
|
|
mBattery = battery;
|
|
|
|
mPower = power;
|
|
|
|
mAlarm = alarm;
|
|
|
|
mActivity = activity;
|
|
|
|
|
|
|
|
context.registerReceiver(new RebootReceiver(),
|
|
|
|
new IntentFilter(REBOOT_ACTION));
|
|
|
|
mRebootIntent = PendingIntent.getBroadcast(context,
|
|
|
|
0, new Intent(REBOOT_ACTION), 0);
|
|
|
|
|
|
|
|
context.registerReceiver(new RebootRequestReceiver(),
|
|
|
|
new IntentFilter(Intent.ACTION_REBOOT),
|
|
|
|
android.Manifest.permission.REBOOT, null);
|
|
|
|
|
|
|
|
mBootTime = System.currentTimeMillis();
|
|
|
|
}
|
|
|
|
|
2010-06-30 14:41:09 -07:00
|
|
|
public void processStarted(String name, int pid) {
|
2009-03-03 19:31:44 -08:00
|
|
|
synchronized (this) {
|
|
|
|
if ("com.android.phone".equals(name)) {
|
|
|
|
mPhonePid = pid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addMonitor(Monitor monitor) {
|
|
|
|
synchronized (this) {
|
|
|
|
if (isAlive()) {
|
|
|
|
throw new RuntimeException("Monitors can't be added while the Watchdog is running");
|
|
|
|
}
|
|
|
|
mMonitors.add(monitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void checkReboot(boolean fromAlarm) {
|
|
|
|
int rebootInterval = mReqRebootInterval >= 0 ? mReqRebootInterval
|
2012-09-26 17:58:19 -07:00
|
|
|
: REBOOT_DEFAULT_INTERVAL;
|
2009-03-03 19:31:44 -08:00
|
|
|
mRebootInterval = rebootInterval;
|
|
|
|
if (rebootInterval <= 0) {
|
|
|
|
// No reboot interval requested.
|
2010-02-26 18:56:32 -08:00
|
|
|
if (localLOGV) Slog.v(TAG, "No need to schedule a reboot alarm!");
|
2009-03-03 19:31:44 -08:00
|
|
|
mAlarm.remove(mRebootIntent);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
long rebootStartTime = mReqRebootStartTime >= 0 ? mReqRebootStartTime
|
2012-09-26 17:58:19 -07:00
|
|
|
: REBOOT_DEFAULT_START_TIME;
|
2009-03-03 19:31:44 -08:00
|
|
|
long rebootWindowMillis = (mReqRebootWindow >= 0 ? mReqRebootWindow
|
2012-09-26 17:58:19 -07:00
|
|
|
: REBOOT_DEFAULT_WINDOW) * 1000;
|
2009-03-03 19:31:44 -08:00
|
|
|
long recheckInterval = (mReqRecheckInterval >= 0 ? mReqRecheckInterval
|
2012-09-26 17:58:19 -07:00
|
|
|
: MEMCHECK_DEFAULT_RECHECK_INTERVAL) * 1000;
|
2009-03-03 19:31:44 -08:00
|
|
|
|
|
|
|
retrieveBrutalityAmount();
|
|
|
|
|
|
|
|
long realStartTime;
|
|
|
|
long now;
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
now = System.currentTimeMillis();
|
|
|
|
realStartTime = computeCalendarTime(mCalendar, now,
|
|
|
|
rebootStartTime);
|
|
|
|
|
|
|
|
long rebootIntervalMillis = rebootInterval*24*60*60*1000;
|
|
|
|
if (DB || mReqRebootNoWait ||
|
|
|
|
(now-mBootTime) >= (rebootIntervalMillis-rebootWindowMillis)) {
|
|
|
|
if (fromAlarm && rebootWindowMillis <= 0) {
|
|
|
|
// No reboot window -- just immediately reboot.
|
2009-12-04 10:31:43 -08:00
|
|
|
EventLog.writeEvent(EventLogTags.WATCHDOG_SCHEDULED_REBOOT, now,
|
2009-03-03 19:31:44 -08:00
|
|
|
(int)rebootIntervalMillis, (int)rebootStartTime*1000,
|
|
|
|
(int)rebootWindowMillis, "");
|
|
|
|
rebootSystem("Checkin scheduled forced");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Are we within the reboot window?
|
|
|
|
if (now < realStartTime) {
|
|
|
|
// Schedule alarm for next check interval.
|
|
|
|
realStartTime = computeCalendarTime(mCalendar,
|
|
|
|
now, rebootStartTime);
|
|
|
|
} else if (now < (realStartTime+rebootWindowMillis)) {
|
|
|
|
String doit = shouldWeBeBrutalLocked(now);
|
2009-12-04 10:31:43 -08:00
|
|
|
EventLog.writeEvent(EventLogTags.WATCHDOG_SCHEDULED_REBOOT, now,
|
2009-03-03 19:31:44 -08:00
|
|
|
(int)rebootInterval, (int)rebootStartTime*1000,
|
|
|
|
(int)rebootWindowMillis, doit != null ? doit : "");
|
|
|
|
if (doit == null) {
|
|
|
|
rebootSystem("Checked scheduled range");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Schedule next alarm either within the window or in the
|
|
|
|
// next interval.
|
|
|
|
if ((now+recheckInterval) >= (realStartTime+rebootWindowMillis)) {
|
|
|
|
realStartTime = computeCalendarTime(mCalendar,
|
|
|
|
now + rebootIntervalMillis, rebootStartTime);
|
|
|
|
} else {
|
|
|
|
realStartTime = now + recheckInterval;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Schedule alarm for next check interval.
|
|
|
|
realStartTime = computeCalendarTime(mCalendar,
|
|
|
|
now + rebootIntervalMillis, rebootStartTime);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-26 18:56:32 -08:00
|
|
|
if (localLOGV) Slog.v(TAG, "Scheduling next reboot alarm for "
|
2009-03-03 19:31:44 -08:00
|
|
|
+ ((realStartTime-now)/1000/60) + "m from now");
|
|
|
|
mAlarm.remove(mRebootIntent);
|
|
|
|
mAlarm.set(AlarmManager.RTC_WAKEUP, realStartTime, mRebootIntent);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Perform a full reboot of the system.
|
|
|
|
*/
|
|
|
|
void rebootSystem(String reason) {
|
2010-02-26 18:56:32 -08:00
|
|
|
Slog.i(TAG, "Rebooting system because: " + reason);
|
2010-03-08 14:48:40 -08:00
|
|
|
PowerManagerService pms = (PowerManagerService) ServiceManager.getService("power");
|
2012-10-03 16:38:22 -07:00
|
|
|
pms.reboot(false, reason, false);
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Load the current Gservices settings for when
|
|
|
|
* {@link #shouldWeBeBrutalLocked} will allow the brutality to happen.
|
|
|
|
* Must not be called with the lock held.
|
|
|
|
*/
|
|
|
|
void retrieveBrutalityAmount() {
|
|
|
|
mMinScreenOff = (mReqMinScreenOff >= 0 ? mReqMinScreenOff
|
2012-09-26 17:58:19 -07:00
|
|
|
: MEMCHECK_DEFAULT_MIN_SCREEN_OFF) * 1000;
|
2009-03-03 19:31:44 -08:00
|
|
|
mMinAlarm = (mReqMinNextAlarm >= 0 ? mReqMinNextAlarm
|
2012-09-26 17:58:19 -07:00
|
|
|
: MEMCHECK_DEFAULT_MIN_ALARM) * 1000;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine whether it is a good time to kill, crash, or otherwise
|
|
|
|
* plunder the current situation for the overall long-term benefit of
|
|
|
|
* the world.
|
|
|
|
*
|
|
|
|
* @param curTime The current system time.
|
|
|
|
* @return Returns null if this is a good time, else a String with the
|
|
|
|
* text of why it is not a good time.
|
|
|
|
*/
|
|
|
|
String shouldWeBeBrutalLocked(long curTime) {
|
2012-10-02 19:11:19 -07:00
|
|
|
if (mBattery == null || !mBattery.isPowered(BatteryManager.BATTERY_PLUGGED_ANY)) {
|
2009-03-03 19:31:44 -08:00
|
|
|
return "battery";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mMinScreenOff >= 0 && (mPower == null ||
|
Power manager rewrite.
The major goal of this rewrite is to make it easier to implement
power management policies correctly. According, the new
implementation primarily uses state-based rather than event-based
triggers for applying changes to the current power state.
For example, when an application requests that the proximity
sensor be used to manage the screen state (by way of a wake lock),
the power manager makes note of the fact that the set of
wake locks changed. Then it executes a common update function
that recalculates the entire state, first looking at wake locks,
then considering user activity, and eventually determining whether
the screen should be turned on or off. At this point it may
make a request to a component called the DisplayPowerController
to asynchronously update the display's powe state. Likewise,
DisplayPowerController makes note of the updated power request
and schedules its own update function to figure out what needs
to be changed.
The big benefit of this approach is that it's easy to mutate
multiple properties of the power state simultaneously then
apply their joint effects together all at once. Transitions
between states are detected and resolved by the update in
a consistent manner.
The new power manager service has is implemented as a set of
loosely coupled components. For the most part, information
only flows one way through these components (by issuing a
request to that component) although some components support
sending a message back to indicate when the work has been
completed. For example, the DisplayPowerController posts
a callback runnable asynchronously to tell the PowerManagerService
when the display is ready. An important feature of this
approach is that each component neatly encapsulates its
state and maintains its own invariants. Moreover, we do
not need to worry about deadlocks or awkward mutual exclusion
semantics because most of the requests are asynchronous.
The benefits of this design are especially apparent in
the implementation of the screen on / off and brightness
control animations which are able to take advantage of
framework features like properties, ObjectAnimator
and Choreographer.
The screen on / off animation is now the responsibility
of the power manager (instead of surface flinger). This change
makes it much easier to ensure that the animation is properly
coordinated with other power state changes and eliminates
the cause of race conditions in the older implementation.
The because of the userActivity() function has been changed
so that it never wakes the device from sleep. This change
removes ambiguity around forcing or disabling user activity
for various purposes. To wake the device, use wakeUp().
To put it to sleep, use goToSleep(). Simple.
The power manager service interface and API has been significantly
simplified and consolidated. Also fixed some inconsistencies
related to how the minimum and maximum screen brightness setting
was presented in brightness control widgets and enforced behind
the scenes.
At present the following features are implemented:
- Wake locks.
- User activity.
- Wake up / go to sleep.
- Power state broadcasts.
- Battery stats and event log notifications.
- Dreams.
- Proximity screen off.
- Animated screen on / off transitions.
- Auto-dimming.
- Auto-brightness control for the screen backlight with
different timeouts for ramping up versus ramping down.
- Auto-on when plugged or unplugged.
- Stay on when plugged.
- Device administration maximum user activity timeout.
- Application controlled brightness via window manager.
The following features are not yet implemented:
- Reduced user activity timeout for the key guard.
- Reduced user activity timeout for the phone application.
- Coordinating screen on barriers with the window manager.
- Preventing auto-rotation during power state changes.
- Auto-brightness adjustment setting (feature was disabled
in previous version of the power manager service pending
an improved UI design so leaving it out for now).
- Interpolated brightness control (a proposed new scheme
for more compactly specifying auto-brightness levels
in config.xml).
- Button / keyboard backlight control.
- Change window manager to associated WorkSource with
KEEP_SCREEN_ON_FLAG wake lock instead of talking
directly to the battery stats service.
- Optionally support animating screen brightness when
turning on/off instead of playing electron beam animation
(config_animateScreenLights).
Change-Id: I1d7a52e98f0449f76d70bf421f6a7f245957d1d7
2012-07-27 15:51:34 -07:00
|
|
|
mPower.timeSinceScreenWasLastOn() < mMinScreenOff)) {
|
2009-03-03 19:31:44 -08:00
|
|
|
return "screen";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mMinAlarm >= 0 && (mAlarm == null ||
|
|
|
|
mAlarm.timeToNextAlarm() < mMinAlarm)) {
|
|
|
|
return "alarm";
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long computeCalendarTime(Calendar c, long curTime,
|
|
|
|
long secondsSinceMidnight) {
|
|
|
|
|
|
|
|
// start with now
|
|
|
|
c.setTimeInMillis(curTime);
|
|
|
|
|
|
|
|
int val = (int)secondsSinceMidnight / (60*60);
|
|
|
|
c.set(Calendar.HOUR_OF_DAY, val);
|
|
|
|
secondsSinceMidnight -= val * (60*60);
|
|
|
|
val = (int)secondsSinceMidnight / 60;
|
|
|
|
c.set(Calendar.MINUTE, val);
|
|
|
|
c.set(Calendar.SECOND, (int)secondsSinceMidnight - (val*60));
|
|
|
|
c.set(Calendar.MILLISECOND, 0);
|
|
|
|
|
|
|
|
long newTime = c.getTimeInMillis();
|
|
|
|
if (newTime < curTime) {
|
|
|
|
// The given time (in seconds since midnight) has already passed for today, so advance
|
|
|
|
// by one day (due to daylight savings, etc., the delta may differ from 24 hours).
|
|
|
|
c.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
newTime = c.getTimeInMillis();
|
|
|
|
}
|
|
|
|
|
|
|
|
return newTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2010-05-28 12:01:56 -07:00
|
|
|
boolean waitedHalf = false;
|
2009-03-03 19:31:44 -08:00
|
|
|
while (true) {
|
|
|
|
mCompleted = false;
|
|
|
|
mHandler.sendEmptyMessage(MONITOR);
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
long timeout = TIME_TO_WAIT;
|
|
|
|
|
|
|
|
// NOTE: We use uptimeMillis() here because we do not want to increment the time we
|
|
|
|
// wait while asleep. If the device is asleep then the thing that we are waiting
|
2010-05-28 12:01:56 -07:00
|
|
|
// to timeout on is asleep as well and won't have a chance to run, causing a false
|
2009-03-03 19:31:44 -08:00
|
|
|
// positive on when to kill things.
|
|
|
|
long start = SystemClock.uptimeMillis();
|
2010-03-04 14:20:31 -08:00
|
|
|
while (timeout > 0 && !mForceKillSystem) {
|
2009-03-03 19:31:44 -08:00
|
|
|
try {
|
2010-03-04 14:20:31 -08:00
|
|
|
wait(timeout); // notifyAll() is called when mForceKillSystem is set
|
2009-03-03 19:31:44 -08:00
|
|
|
} catch (InterruptedException e) {
|
2010-03-04 14:20:31 -08:00
|
|
|
Log.wtf(TAG, e);
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
timeout = TIME_TO_WAIT - (SystemClock.uptimeMillis() - start);
|
2010-03-04 14:20:31 -08:00
|
|
|
}
|
2009-03-03 19:31:44 -08:00
|
|
|
|
|
|
|
if (mCompleted && !mForceKillSystem) {
|
|
|
|
// The monitors have returned.
|
2010-05-28 12:01:56 -07:00
|
|
|
waitedHalf = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!waitedHalf) {
|
|
|
|
// We've waited half the deadlock-detection interval. Pull a stack
|
|
|
|
// trace and wait another half.
|
2010-08-31 15:40:21 -07:00
|
|
|
ArrayList<Integer> pids = new ArrayList<Integer>();
|
2010-05-28 12:01:56 -07:00
|
|
|
pids.add(Process.myPid());
|
2012-06-08 17:23:59 -07:00
|
|
|
ActivityManagerService.dumpStackTraces(true, pids, null, null,
|
|
|
|
NATIVE_STACKS_OF_INTEREST);
|
2010-05-28 12:01:56 -07:00
|
|
|
waitedHalf = true;
|
2009-03-03 19:31:44 -08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we got here, that means that the system is most likely hung.
|
2012-09-04 13:35:12 -07:00
|
|
|
// First collect stack traces from all threads of the system process.
|
|
|
|
// Then kill this process so that the system will restart.
|
2010-03-04 14:20:31 -08:00
|
|
|
|
2011-01-14 11:28:22 -08:00
|
|
|
final String name = (mCurrentMonitor != null) ?
|
2010-08-31 15:40:21 -07:00
|
|
|
mCurrentMonitor.getClass().getName() : "null";
|
2009-12-04 10:31:43 -08:00
|
|
|
EventLog.writeEvent(EventLogTags.WATCHDOG, name);
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2010-08-31 15:40:21 -07:00
|
|
|
ArrayList<Integer> pids = new ArrayList<Integer>();
|
2010-03-04 14:20:31 -08:00
|
|
|
pids.add(Process.myPid());
|
2010-03-11 22:00:47 -08:00
|
|
|
if (mPhonePid > 0) pids.add(mPhonePid);
|
2010-05-28 12:01:56 -07:00
|
|
|
// Pass !waitedHalf so that just in case we somehow wind up here without having
|
|
|
|
// dumped the halfway stacks, we properly re-initialize the trace file.
|
2011-01-14 11:28:22 -08:00
|
|
|
final File stack = ActivityManagerService.dumpStackTraces(
|
2012-06-08 17:23:59 -07:00
|
|
|
!waitedHalf, pids, null, null, NATIVE_STACKS_OF_INTEREST);
|
2010-03-11 22:00:47 -08:00
|
|
|
|
|
|
|
// Give some extra time to make sure the stack traces get written.
|
|
|
|
// The system's been hanging for a minute, another second or two won't hurt much.
|
|
|
|
SystemClock.sleep(2000);
|
|
|
|
|
2010-06-04 14:55:02 -07:00
|
|
|
// Pull our own kernel thread stacks as well if we're configured for that
|
|
|
|
if (RECORD_KERNEL_THREADS) {
|
|
|
|
dumpKernelStackTraces();
|
|
|
|
}
|
|
|
|
|
2012-11-29 11:42:11 -08:00
|
|
|
// Trigger the kernel to dump all blocked threads to the kernel log
|
|
|
|
try {
|
|
|
|
FileWriter sysrq_trigger = new FileWriter("/proc/sysrq-trigger");
|
|
|
|
sysrq_trigger.write("w");
|
|
|
|
sysrq_trigger.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
Slog.e(TAG, "Failed to write to /proc/sysrq-trigger");
|
|
|
|
Slog.e(TAG, e.getMessage());
|
|
|
|
}
|
|
|
|
|
2011-01-14 11:28:22 -08:00
|
|
|
// Try to add the error to the dropbox, but assuming that the ActivityManager
|
|
|
|
// itself may be deadlocked. (which has happened, causing this statement to
|
|
|
|
// deadlock and the watchdog as a whole to be ineffective)
|
|
|
|
Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
|
|
|
|
public void run() {
|
|
|
|
mActivity.addErrorToDropBox(
|
2011-10-28 11:12:06 -07:00
|
|
|
"watchdog", null, "system_server", null, null,
|
|
|
|
name, null, stack, null);
|
2011-01-14 11:28:22 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
dropboxThread.start();
|
|
|
|
try {
|
|
|
|
dropboxThread.join(2000); // wait up to 2 seconds for it to return.
|
|
|
|
} catch (InterruptedException ignored) {}
|
2009-03-03 19:31:44 -08:00
|
|
|
|
2012-09-04 13:35:12 -07:00
|
|
|
// Only kill the process if the debugger is not attached.
|
2009-03-03 19:31:44 -08:00
|
|
|
if (!Debug.isDebuggerConnected()) {
|
2010-03-04 14:20:31 -08:00
|
|
|
Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + name);
|
2012-09-04 13:35:12 -07:00
|
|
|
Process.killProcess(Process.myPid());
|
|
|
|
System.exit(10);
|
2010-03-04 14:20:31 -08:00
|
|
|
} else {
|
|
|
|
Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
2010-05-28 12:01:56 -07:00
|
|
|
|
|
|
|
waitedHalf = false;
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|
|
|
|
}
|
2010-06-04 14:55:02 -07:00
|
|
|
|
|
|
|
private File dumpKernelStackTraces() {
|
|
|
|
String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
|
|
|
|
if (tracesPath == null || tracesPath.length() == 0) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
native_dumpKernelStacks(tracesPath);
|
|
|
|
return new File(tracesPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
private native void native_dumpKernelStacks(String tracesPath);
|
2009-03-03 19:31:44 -08:00
|
|
|
}
|