Cleanup some of the thread merging.

Adds an optimization for checking whether a looper is stuck,
with a new Looper method to see if its thread is currently
idle.  This will allow us to put a large number of loopers
in the monitor efficiently, since we generally won't have to
do a context switch on each of them (since most looper threads
spend most of their time idle waiting for work).

Also change things so the system process's main thread
is actually running on the main thread.  Because Jeff
asked for this, and who am I to argue? :)

Change-Id: I12999e6f9c4b056c22dd652cb78c2453c391061f
diff --git a/cmds/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp
index 745c34a..3e52d30 100644
--- a/cmds/system_server/library/system_init.cpp
+++ b/cmds/system_server/library/system_init.cpp
@@ -94,12 +94,12 @@
     if (methodId == NULL) {
         return UNKNOWN_ERROR;
     }
-    env->CallStaticVoidMethod(clazz, methodId);
 
-    ALOGI("System server: entering thread pool.\n");
+    ALOGI("System server: entering thread pool.");
     ProcessState::self()->startThreadPool();
-    IPCThreadState::self()->joinThreadPool();
-    ALOGI("System server: exiting thread pool.\n");
+
+    // This is the main thread of the system server, and will never exit.
+    env->CallStaticVoidMethod(clazz, methodId);
 
     return NO_ERROR;
 }
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index d5cf771..78c859e 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -289,6 +289,16 @@
         return mQueue;
     }
 
+    /**
+     * Return whether this looper's thread is currently idle, waiting for new work
+     * to do.  This is intrinsically racy, since its state can change before you get
+     * the result back.
+     * @hide
+     */
+    public boolean isIdling() {
+        return mQueue.isIdling();
+    }
+
     public void dump(Printer pw, String prefix) {
         pw = PrefixPrinter.create(pw, prefix);
         pw.println(this.toString());
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index bf7e5ca..1e8983e 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -52,6 +52,7 @@
     private native static void nativeDestroy(int ptr);
     private native static void nativePollOnce(int ptr, int timeoutMillis);
     private native static void nativeWake(int ptr);
+    private native static boolean nativeIsIdling(int ptr);
 
     /**
      * Callback interface for discovering when a thread is going to block
@@ -379,6 +380,10 @@
         }
     }
 
+    boolean isIdling() {
+        return nativeIsIdling(mPtr);
+    }
+
     void removeMessages(Handler h, int what, Object object) {
         if (h == null) {
             return;
diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp
index 7540645..c9c3720 100644
--- a/core/jni/android_os_MessageQueue.cpp
+++ b/core/jni/android_os_MessageQueue.cpp
@@ -141,6 +141,11 @@
     return nativeMessageQueue->wake();
 }
 
+static jboolean android_os_MessageQueue_nativeIsIdling(JNIEnv* env, jclass clazz, jint ptr) {
+    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
+    return nativeMessageQueue->getLooper()->isIdling();
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMessageQueueMethods[] = {
@@ -148,7 +153,8 @@
     { "nativeInit", "()I", (void*)android_os_MessageQueue_nativeInit },
     { "nativeDestroy", "(I)V", (void*)android_os_MessageQueue_nativeDestroy },
     { "nativePollOnce", "(II)V", (void*)android_os_MessageQueue_nativePollOnce },
-    { "nativeWake", "(I)V", (void*)android_os_MessageQueue_nativeWake }
+    { "nativeWake", "(I)V", (void*)android_os_MessageQueue_nativeWake },
+    { "nativeIsIdling", "(I)Z", (void*)android_os_MessageQueue_nativeIsIdling }
 };
 
 #define FIND_CLASS(var, className) \
diff --git a/services/java/com/android/server/IoThread.java b/services/java/com/android/server/IoThread.java
index b443578..09f2af7 100644
--- a/services/java/com/android/server/IoThread.java
+++ b/services/java/com/android/server/IoThread.java
@@ -47,14 +47,14 @@
     }
 
     public static IoThread get() {
-        synchronized (UiThread.class) {
+        synchronized (IoThread.class) {
             ensureThreadLocked();
             return sInstance;
         }
     }
 
     public static Handler getHandler() {
-        synchronized (UiThread.class) {
+        synchronized (IoThread.class) {
             ensureThreadLocked();
             return sHandler;
         }
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 1d1b6b9..7857d6c 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -37,6 +37,7 @@
 import android.os.Environment;
 import android.os.Environment.UserEnvironment;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -1311,7 +1312,9 @@
         // XXX: This will go away soon in favor of IMountServiceObserver
         mPms = (PackageManagerService) ServiceManager.getService("package");
 
-        mHandler = new MountServiceHandler(IoThread.get().getLooper());
+        HandlerThread hthread = new HandlerThread(TAG);
+        hthread.start();
+        mHandler = new MountServiceHandler(hthread.getLooper());
 
         // Watch for user changes
         final IntentFilter userFilter = new IntentFilter();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 89a6f60..7d7aeec 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -75,7 +75,7 @@
 import java.util.Timer;
 import java.util.TimerTask;
 
-class ServerThread extends Thread {
+class ServerThread {
     private static final String TAG = "SystemServer";
     private static final String ENCRYPTING_STATE = "trigger_restart_min_framework";
     private static final String ENCRYPTED_STATE = "1";
@@ -87,8 +87,7 @@
         Log.wtf(TAG, "BOOT FAILURE " + msg, e);
     }
 
-    @Override
-    public void run() {
+    public void initAndLoop() {
         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN,
             SystemClock.uptimeMillis());
 
@@ -1119,8 +1118,10 @@
 
     public static final void init2() {
         Slog.i(TAG, "Entered the Android system server!");
-        Thread thr = new ServerThread();
-        thr.setName("android.server.ServerThread");
-        thr.start();
+
+        // This used to be its own separate thread, but now it is
+        // just the loop we run on the main thread.
+        ServerThread thr = new ServerThread();
+        thr.initAndLoop();
     }
 }
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index 551d9f6c7..7eedc2a 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -33,7 +33,6 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.Message;
 import android.os.Process;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -135,6 +134,16 @@
         }
 
         public void scheduleCheckLocked() {
+            if (!mCheckReboot && mMonitors.size() == 0 && mHandler.getLooper().isIdling()) {
+                // If the target looper is or just recently was idling, then
+                // there is no reason to enqueue our checker on it since that
+                // is as good as it not being deadlocked.  This avoid having
+                // to do a context switch to check the thread.  Note that we
+                // only do this if mCheckReboot is false and we have no
+                // monitors, since those would need to be executed at this point.
+                mCompleted = true;
+                return;
+            }
             mCompleted = false;
             mCurrentMonitor = null;
             mHandler.postAtFrontOfQueue(this);
diff --git a/services/java/com/android/server/usb/UsbDebuggingManager.java b/services/java/com/android/server/usb/UsbDebuggingManager.java
index ba3f1d1..ce953a4 100644
--- a/services/java/com/android/server/usb/UsbDebuggingManager.java
+++ b/services/java/com/android/server/usb/UsbDebuggingManager.java
@@ -161,7 +161,7 @@
 
                     mAdbEnabled = true;
 
-                    mThread = new Thread(UsbDebuggingManager.this, "UsbDebuggingManager");
+                    mThread = new Thread(UsbDebuggingManager.this, TAG);
                     mThread.start();
 
                     break;