Start combining threads in system process.
This introduces four generic thread that services can
use in the system process:
- Background: part of the framework for all processes, for
work that is purely background (no timing constraint).
- UI: for time-critical display of UI.
- Foreground: normal foreground work.
- IO: performing IO operations.
I went through and moved services into these threads in the
places I felt relatively comfortable about understanding what
they are doing. There are still a bunch more we need to look
at -- lots of networking stuff left, 3 or so different native
daemon connectors which I didn't know how much would block,
audio stuff, etc.
Also updated Watchdog to be aware of and check these new
threads, with a new API for other threads to also participate
in this checking.
Change-Id: Ie2f11061cebde5f018d7383b3a910fbbd11d5e11
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index d4d3850..fc95790 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -80,15 +80,13 @@
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>();
+ final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<HandlerChecker>();
+ final HandlerChecker mMonitorChecker;
ContentResolver mResolver;
BatteryService mBattery;
PowerManagerService mPower;
AlarmManagerService mAlarm;
ActivityManagerService mActivity;
- boolean mCompleted;
- Monitor mCurrentMonitor;
int mPhonePid;
@@ -111,40 +109,65 @@
int mReqRecheckInterval= -1; // >= 0 if a specific recheck interval has been requested
/**
- * Used for scheduling monitor callbacks and checking memory usage.
+ * Used for checking status of handle threads and scheduling monitor callbacks.
*/
- final class HeartbeatHandler extends Handler {
- HeartbeatHandler(Looper looper) {
- super(looper);
+ public final class HandlerChecker implements Runnable {
+ private final Handler mHandler;
+ private final String mName;
+ private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
+ private final boolean mCheckReboot;
+ private boolean mCompleted;
+ private Monitor mCurrentMonitor;
+
+ HandlerChecker(Handler handler, String name, boolean checkReboot) {
+ mHandler = handler;
+ mName = name;
+ mCheckReboot = checkReboot;
+ }
+
+ public void addMonitor(Monitor monitor) {
+ mMonitors.add(monitor);
+ }
+
+ public void scheduleCheckLocked() {
+ mCompleted = false;
+ mCurrentMonitor = null;
+ mHandler.postAtFrontOfQueue(this);
+ }
+
+ public boolean isCompletedLocked() {
+ return mCompleted;
+ }
+
+ public String describeBlockedStateLocked() {
+ return mCurrentMonitor == null ? mName : mCurrentMonitor.getClass().getName();
}
@Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MONITOR: {
- // See if we should force a reboot.
- int rebootInterval = mReqRebootInterval >= 0
- ? mReqRebootInterval : REBOOT_DEFAULT_INTERVAL;
- if (mRebootInterval != rebootInterval) {
- mRebootInterval = rebootInterval;
- // We have been running long enough that a reboot can
- // be considered...
- checkReboot(false);
- }
+ public void run() {
+ // See if we should force a reboot.
+ if (mCheckReboot) {
+ int rebootInterval = mReqRebootInterval >= 0
+ ? mReqRebootInterval : REBOOT_DEFAULT_INTERVAL;
+ 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++) {
- synchronized (Watchdog.this) {
- mCurrentMonitor = mMonitors.get(i);
- }
- mCurrentMonitor.monitor();
- }
+ final int size = mMonitors.size();
+ for (int i = 0 ; i < size ; i++) {
+ synchronized (Watchdog.this) {
+ mCurrentMonitor = mMonitors.get(i);
+ }
+ mCurrentMonitor.monitor();
+ }
- synchronized (Watchdog.this) {
- mCompleted = true;
- mCurrentMonitor = null;
- }
- } break;
+ synchronized (Watchdog.this) {
+ mCompleted = true;
+ mCurrentMonitor = null;
}
}
}
@@ -189,9 +212,23 @@
private Watchdog() {
super("watchdog");
- // Explicitly bind the HeartbeatHandler to run on the ServerThread, so
- // that it can't get accidentally bound to another thread.
- mHandler = new HeartbeatHandler(Looper.getMainLooper());
+ // Initialize handler checkers for each common thread we want to check. Note
+ // that we are not currently checking the background thread, since it can
+ // potentially hold longer running operations with no guarantees about the timeliness
+ // of operations there.
+
+ // The shared foreground thread is the main checker. It is where we
+ // will also dispatch monitor checks and do other work.
+ mMonitorChecker = new HandlerChecker(FgThread.getHandler(), "foreground thread", true);
+ mHandlerCheckers.add(mMonitorChecker);
+ // Add checker for main thread. We only do a quick check since there
+ // can be UI running on the thread.
+ mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
+ "main thread", false));
+ // Add checker for shared UI thread.
+ mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(), "ui thread", false));
+ // And also check IO thread.
+ mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(), "i/o thread", false));
}
public void init(Context context, BatteryService battery,
@@ -226,9 +263,18 @@
public void addMonitor(Monitor monitor) {
synchronized (this) {
if (isAlive()) {
- throw new RuntimeException("Monitors can't be added while the Watchdog is running");
+ throw new RuntimeException("Monitors can't be added once the Watchdog is running");
}
- mMonitors.add(monitor);
+ mMonitorChecker.addMonitor(monitor);
+ }
+ }
+
+ public void addThread(Handler thread, String name) {
+ synchronized (this) {
+ if (isAlive()) {
+ throw new RuntimeException("Threads can't be added once the Watchdog is running");
+ }
+ mHandlerCheckers.add(new HandlerChecker(thread, name, false));
}
}
@@ -382,6 +428,30 @@
return newTime;
}
+ private boolean haveAllCheckersCompletedLocked() {
+ for (int i=0; i<mHandlerCheckers.size(); i++) {
+ HandlerChecker hc = mHandlerCheckers.get(i);
+ if (!hc.isCompletedLocked()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private String describeBlockedCheckersLocked() {
+ StringBuilder builder = new StringBuilder(128);
+ for (int i=0; i<mHandlerCheckers.size(); i++) {
+ HandlerChecker hc = mHandlerCheckers.get(i);
+ if (!hc.isCompletedLocked()) {
+ if (builder.length() > 0) {
+ builder.append(", ");
+ }
+ builder.append(hc.describeBlockedStateLocked());
+ }
+ }
+ return builder.toString();
+ }
+
@Override
public void run() {
boolean waitedHalf = false;
@@ -389,8 +459,14 @@
final String name;
synchronized (this) {
long timeout = TIME_TO_WAIT;
- mCompleted = false;
- mHandler.sendEmptyMessage(MONITOR);
+ if (!waitedHalf) {
+ // If we are not at the half-point of waiting, perform a
+ // new set of checks. Otherwise we are still waiting for a previous set.
+ for (int i=0; i<mHandlerCheckers.size(); i++) {
+ HandlerChecker hc = mHandlerCheckers.get(i);
+ hc.scheduleCheckLocked();
+ }
+ }
// 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
@@ -406,7 +482,7 @@
timeout = TIME_TO_WAIT - (SystemClock.uptimeMillis() - start);
}
- if (mCompleted) {
+ if (haveAllCheckersCompletedLocked()) {
// The monitors have returned.
waitedHalf = false;
continue;
@@ -423,8 +499,7 @@
continue;
}
- name = (mCurrentMonitor != null) ?
- mCurrentMonitor.getClass().getName() : "main thread blocked";
+ name = describeBlockedCheckersLocked();
}
// If we got here, that means that the system is most likely hung.