Improve activity manager debug dumps.

Activity manager now does all dump requests into apps
asynchronously, so it can nicely timeout if there is an
app problem.  Also lots of general cleanup of the am
dump output.

Change-Id: Id0dbccffb217315aeb85c964e379833e6aa3f5af
diff --git a/api/current.xml b/api/current.xml
index 2b10d42..23c7af8 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -143541,6 +143541,21 @@
 <parameter name="args" type="java.lang.String[]">
 </parameter>
 </method>
+<method name="dumpAsync"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fd" type="java.io.FileDescriptor">
+</parameter>
+<parameter name="args" type="java.lang.String[]">
+</parameter>
+</method>
 <method name="flushPendingCommands"
  return="void"
  abstract="false"
@@ -148013,6 +148028,23 @@
 <exception name="RemoteException" type="android.os.RemoteException">
 </exception>
 </method>
+<method name="dumpAsync"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fd" type="java.io.FileDescriptor">
+</parameter>
+<parameter name="args" type="java.lang.String[]">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
 <method name="getInterfaceDescriptor"
  return="java.lang.String"
  abstract="true"
@@ -150367,6 +150399,21 @@
  visibility="public"
 >
 </method>
+<method name="dup"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="orig" type="java.io.FileDescriptor">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
 <method name="fromSocket"
  return="android.os.ParcelFileDescriptor"
  abstract="false"
@@ -267896,7 +267943,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 533a60a..df7037c 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -185,11 +185,16 @@
     run_command("DUMPSYS", 60, "dumpsys", NULL);
 
     printf("========================================================\n");
-    printf("== Application Services\n");
+    printf("== Running Application Activities\n");
     printf("========================================================\n");
 
-    /* Instead of a 60s timeout, we should give each service a 5 second timeout */
-    run_command("APP SERVICES", 60, "dumpsys", "activity", "service", NULL);
+    run_command("APP ACTIVITIES", 30, "dumpsys", "activity", "all", NULL);
+
+    printf("========================================================\n");
+    printf("== Running Application Services\n");
+    printf("========================================================\n");
+
+    run_command("APP SERVICES", 30, "dumpsys", "activity", "service", "all", NULL);
 
 }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 751726a..5e4e36e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -363,11 +363,10 @@
     }
 
     private static final class DumpComponentInfo {
-        FileDescriptor fd;
+        ParcelFileDescriptor fd;
         IBinder token;
         String prefix;
         String[] args;
-        boolean dumped;
     }
 
     private static final class ResultData {
@@ -621,20 +620,13 @@
 
         public void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args) {
             DumpComponentInfo data = new DumpComponentInfo();
-            data.fd = fd;
-            data.token = servicetoken;
-            data.args = args;
-            data.dumped = false;
-            queueOrSendMessage(H.DUMP_SERVICE, data);
-            synchronized (data) {
-                while (!data.dumped) {
-                    try {
-                        data.wait();
-                    } catch (InterruptedException e) {
-                        // no need to do anything here, we will keep waiting until
-                        // dumped is set
-                    }
-                }
+            try {
+                data.fd = ParcelFileDescriptor.dup(fd);
+                data.token = servicetoken;
+                data.args = args;
+                queueOrSendMessage(H.DUMP_SERVICE, data);
+            } catch (IOException e) {
+                Slog.w(TAG, "dumpService failed", e);
             }
         }
 
@@ -696,21 +688,14 @@
         public void dumpActivity(FileDescriptor fd, IBinder activitytoken,
                 String prefix, String[] args) {
             DumpComponentInfo data = new DumpComponentInfo();
-            data.fd = fd;
-            data.token = activitytoken;
-            data.prefix = prefix;
-            data.args = args;
-            data.dumped = false;
-            queueOrSendMessage(H.DUMP_ACTIVITY, data);
-            synchronized (data) {
-                while (!data.dumped) {
-                    try {
-                        data.wait();
-                    } catch (InterruptedException e) {
-                        // no need to do anything here, we will keep waiting until
-                        // dumped is set
-                    }
-                }
+            try {
+                data.fd = ParcelFileDescriptor.dup(fd);
+                data.token = activitytoken;
+                data.prefix = prefix;
+                data.args = args;
+                queueOrSendMessage(H.DUMP_ACTIVITY, data);
+            } catch (IOException e) {
+                Slog.w(TAG, "dumpActivity failed", e);
             }
         }
 
@@ -2113,33 +2098,27 @@
     }
 
     private void handleDumpService(DumpComponentInfo info) {
-        try {
-            Service s = mServices.get(info.token);
-            if (s != null) {
-                PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd));
-                s.dump(info.fd, pw, info.args);
-                pw.close();
-            }
-        } finally {
-            synchronized (info) {
-                info.dumped = true;
-                info.notifyAll();
+        Service s = mServices.get(info.token);
+        if (s != null) {
+            PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd.getFileDescriptor()));
+            s.dump(info.fd.getFileDescriptor(), pw, info.args);
+            pw.flush();
+            try {
+                info.fd.close();
+            } catch (IOException e) {
             }
         }
     }
 
     private void handleDumpActivity(DumpComponentInfo info) {
-        try {
-            ActivityClientRecord r = mActivities.get(info.token);
-            if (r != null && r.activity != null) {
-                PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd));
-                r.activity.dump(info.prefix, info.fd, pw, info.args);
-                pw.close();
-            }
-        } finally {
-            synchronized (info) {
-                info.dumped = true;
-                info.notifyAll();
+        ActivityClientRecord r = mActivities.get(info.token);
+        if (r != null && r.activity != null) {
+            PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd.getFileDescriptor()));
+            r.activity.dump(info.prefix, info.fd.getFileDescriptor(), pw, info.args);
+            pw.flush();
+            try {
+                info.fd.close();
+            } catch (IOException e) {
             }
         }
     }
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index aa26b04..a82234e 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -822,7 +822,7 @@
         data.writeFileDescriptor(fd);
         data.writeStrongBinder(token);
         data.writeStringArray(args);
-        mRemote.transact(DUMP_SERVICE_TRANSACTION, data, null, 0);
+        mRemote.transact(DUMP_SERVICE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
         data.recycle();
     }
     
@@ -944,7 +944,7 @@
         data.writeStrongBinder(token);
         data.writeString(prefix);
         data.writeStringArray(args);
-        mRemote.transact(DUMP_ACTIVITY_TRANSACTION, data, null, 0);
+        mRemote.transact(DUMP_ACTIVITY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
         data.recycle();
     }
 
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 28ba4e7..b9dfd72 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -299,45 +299,106 @@
     
     public String toString() {
         StringBuilder sb = new StringBuilder(128);
-        sb.append("{ scale=");
+        sb.append("{ fnt=");
         sb.append(fontScale);
         sb.append(" imsi=");
         sb.append(mcc);
         sb.append("/");
         sb.append(mnc);
-        sb.append(" loc=");
-        sb.append(locale);
-        sb.append(" touch=");
-        sb.append(touchscreen);
-        sb.append(" keys=");
-        sb.append(keyboard);
-        sb.append("/");
-        sb.append(keyboardHidden);
-        sb.append("/");
-        sb.append(hardKeyboardHidden);
-        sb.append(" nav=");
-        sb.append(navigation);
-        sb.append("/");
-        sb.append(navigationHidden);
-        sb.append(" orien=");
-        switch(orientation) {
-            case ORIENTATION_LANDSCAPE:
-                sb.append("L"); break;
-            case ORIENTATION_PORTRAIT:
-                sb.append("P"); break;
-            default:
-                sb.append(orientation);
+        if (locale != null) {
+            sb.append(" ");
+            sb.append(locale);
+        } else {
+            sb.append(" (no locale)");
         }
-        sb.append(" layout=0x");
-        sb.append(java.lang.Integer.toHexString(screenLayout));
-        sb.append(" uiMode=0x");
-        sb.append(java.lang.Integer.toHexString(uiMode));
-        sb.append(" wdp=");
-        sb.append(screenWidthDp);
-        sb.append(" hdp=");
-        sb.append(screenHeightDp);
+        switch (touchscreen) {
+            case TOUCHSCREEN_UNDEFINED: sb.append(" ?touch"); break;
+            case TOUCHSCREEN_NOTOUCH: sb.append(" -touch"); break;
+            case TOUCHSCREEN_STYLUS: sb.append(" stylus"); break;
+            case TOUCHSCREEN_FINGER: sb.append(" finger"); break;
+            default: sb.append(" touch="); sb.append(touchscreen); break;
+        }
+        switch (keyboard) {
+            case KEYBOARD_UNDEFINED: sb.append(" ?keyb"); break;
+            case KEYBOARD_NOKEYS: sb.append(" -keyb"); break;
+            case KEYBOARD_QWERTY: sb.append(" qwerty"); break;
+            case KEYBOARD_12KEY: sb.append(" 12key"); break;
+            default: sb.append(" keys="); sb.append(keyboard); break;
+        }
+        switch (keyboardHidden) {
+            case KEYBOARDHIDDEN_UNDEFINED: sb.append("/?"); break;
+            case KEYBOARDHIDDEN_NO: sb.append("/v"); break;
+            case KEYBOARDHIDDEN_YES: sb.append("/h"); break;
+            case KEYBOARDHIDDEN_SOFT: sb.append("/s"); break;
+            default: sb.append("/"); sb.append(keyboardHidden); break;
+        }
+        switch (hardKeyboardHidden) {
+            case HARDKEYBOARDHIDDEN_UNDEFINED: sb.append("/?"); break;
+            case HARDKEYBOARDHIDDEN_NO: sb.append("/v"); break;
+            case HARDKEYBOARDHIDDEN_YES: sb.append("/h"); break;
+            default: sb.append("/"); sb.append(hardKeyboardHidden); break;
+        }
+        switch (navigation) {
+            case NAVIGATION_UNDEFINED: sb.append(" ?nav"); break;
+            case NAVIGATION_NONAV: sb.append(" -nav"); break;
+            case NAVIGATION_DPAD: sb.append(" dpad"); break;
+            case NAVIGATION_TRACKBALL: sb.append(" tball"); break;
+            case NAVIGATION_WHEEL: sb.append(" wheel"); break;
+            default: sb.append(" nav="); sb.append(navigation); break;
+        }
+        switch (navigationHidden) {
+            case NAVIGATIONHIDDEN_UNDEFINED: sb.append("/?"); break;
+            case NAVIGATIONHIDDEN_NO: sb.append("/v"); break;
+            case NAVIGATIONHIDDEN_YES: sb.append("/h"); break;
+            default: sb.append("/"); sb.append(navigationHidden); break;
+        }
+        switch (orientation) {
+            case ORIENTATION_UNDEFINED: sb.append(" ?orien"); break;
+            case ORIENTATION_LANDSCAPE: sb.append(" land"); break;
+            case ORIENTATION_PORTRAIT: sb.append(" port"); break;
+            default: sb.append(" orien="); sb.append(orientation); break;
+        }
+        switch ((screenLayout&SCREENLAYOUT_SIZE_MASK)) {
+            case SCREENLAYOUT_SIZE_UNDEFINED: sb.append(" ?lsize"); break;
+            case SCREENLAYOUT_SIZE_SMALL: sb.append(" smll"); break;
+            case SCREENLAYOUT_SIZE_NORMAL: sb.append(" nrml"); break;
+            case SCREENLAYOUT_SIZE_LARGE: sb.append(" lrg"); break;
+            case SCREENLAYOUT_SIZE_XLARGE: sb.append(" xlrg"); break;
+            default: sb.append(" layoutSize=");
+                    sb.append(screenLayout&SCREENLAYOUT_SIZE_MASK); break;
+        }
+        switch ((screenLayout&SCREENLAYOUT_LONG_MASK)) {
+            case SCREENLAYOUT_LONG_UNDEFINED: sb.append(" ?long"); break;
+            case SCREENLAYOUT_LONG_NO: /* not-long is not interesting to print */ break;
+            case SCREENLAYOUT_LONG_YES: sb.append(" long"); break;
+            default: sb.append(" layoutLong=");
+                    sb.append(screenLayout&SCREENLAYOUT_LONG_MASK); break;
+        }
+        switch ((uiMode&UI_MODE_TYPE_MASK)) {
+            case UI_MODE_TYPE_UNDEFINED: sb.append(" ?uimode"); break;
+            case UI_MODE_TYPE_NORMAL: /* normal is not interesting to print */ break;
+            case UI_MODE_TYPE_DESK: sb.append(" desk"); break;
+            case UI_MODE_TYPE_CAR: sb.append(" car"); break;
+            default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break;
+        }
+        switch ((uiMode&UI_MODE_NIGHT_MASK)) {
+            case UI_MODE_NIGHT_UNDEFINED: sb.append(" ?night"); break;
+            case UI_MODE_NIGHT_NO: /* not-night is not interesting to print */ break;
+            case UI_MODE_NIGHT_YES: sb.append(" night"); break;
+            default: sb.append(" night="); sb.append(uiMode&UI_MODE_NIGHT_MASK); break;
+        }
+        if (screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED) {
+            sb.append(" w"); sb.append(screenWidthDp); sb.append("dp");
+        } else {
+            sb.append("?wdp");
+        }
+        if (screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
+            sb.append(" h"); sb.append(screenHeightDp); sb.append("dp");
+        } else {
+            sb.append("?hdp");
+        }
         if (seq != 0) {
-            sb.append(" seq=");
+            sb.append(" s.");
             sb.append(seq);
         }
         sb.append('}');
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 7dc36f9..ae1e1c2 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -256,6 +256,25 @@
     }
     
     /**
+     * Like {@link #dump(FileDescriptor, String[])}, but ensures the target
+     * executes asynchronously.
+     */
+    public void dumpAsync(final FileDescriptor fd, final String[] args) {
+        final FileOutputStream fout = new FileOutputStream(fd);
+        final PrintWriter pw = new PrintWriter(fout);
+        Thread thr = new Thread("Binder.dumpAsync") {
+            public void run() {
+                try {
+                    dump(fd, pw, args);
+                } finally {
+                    pw.flush();
+                }
+            }
+        };
+        thr.start();
+    }
+
+    /**
      * Print the object's state into the given stream.
      * 
      * @param fd The raw file descriptor that the dump is being sent to.
@@ -364,6 +383,20 @@
         }
     }
     
+    public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeFileDescriptor(fd);
+        data.writeStringArray(args);
+        try {
+            transact(DUMP_TRANSACTION, data, reply, FLAG_ONEWAY);
+            reply.readException();
+        } finally {
+            data.recycle();
+            reply.recycle();
+        }
+    }
+
     BinderProxy() {
         mSelf = new WeakReference(this);
     }
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 8ae8008..8876354 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -157,6 +157,16 @@
     public void dump(FileDescriptor fd, String[] args) throws RemoteException;
     
     /**
+     * Like {@link #dump(FileDescriptor, String[])} but always executes
+     * asynchronously.  If the object is local, a new thread is created
+     * to perform the dump.
+     *
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param args additional arguments to the dump request.
+     */
+    public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException;
+
+    /**
      * Perform a generic operation with the object.
      * 
      * @param code The action to perform.  This should
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index eca34848..6b35215 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -1383,6 +1383,8 @@
     private native FileDescriptor internalReadFileDescriptor();
     /*package*/ static native FileDescriptor openFileDescriptor(String file,
             int mode) throws FileNotFoundException;
+    /*package*/ static native FileDescriptor dupFileDescriptor(FileDescriptor orig)
+            throws IOException;
     /*package*/ static native void closeFileDescriptor(FileDescriptor desc)
             throws IOException;
     /*package*/ static native void clearFileDescriptor(FileDescriptor desc);
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 5bd129f..aa959b4 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -117,6 +117,17 @@
     }
 
     /**
+     * Create a new ParcelFileDescriptor that is a dup of an existing
+     * FileDescriptor.  This obeys standard POSIX semantics, where the
+     * new file descriptor shared state such as file position with the
+     * original file descriptor.
+     */
+    public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException {
+        FileDescriptor fd = Parcel.dupFileDescriptor(orig);
+        return fd != null ? new ParcelFileDescriptor(fd) : null;
+    }
+
+    /**
      * Create a new ParcelFileDescriptor from the specified Socket.
      *
      * @param socket The Socket whose FileDescriptor is used to create
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 09a5fd41..eb1c437 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -1509,7 +1509,31 @@
 
     int fd = open(name8.string(), flags, realMode);
     if (fd < 0) {
-        jniThrowException(env, "java/io/FileNotFoundException", NULL);
+        jniThrowException(env, "java/io/FileNotFoundException", strerror(errno));
+        return NULL;
+    }
+    jobject object = newFileDescriptor(env, fd);
+    if (object == NULL) {
+        close(fd);
+    }
+    return object;
+}
+
+static jobject android_os_Parcel_dupFileDescriptor(JNIEnv* env, jobject clazz, jobject orig)
+{
+    if (orig == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return NULL;
+    }
+    int origfd = env->GetIntField(orig, gFileDescriptorOffsets.mDescriptor);
+    if (origfd < 0) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "bad FileDescriptor");
+        return NULL;
+    }
+
+    int fd = dup(origfd);
+    if (fd < 0) {
+        jniThrowException(env, "java/io/IOException", strerror(errno));
         return NULL;
     }
     jobject object = newFileDescriptor(env, fd);
@@ -1521,6 +1545,10 @@
 
 static void android_os_Parcel_closeFileDescriptor(JNIEnv* env, jobject clazz, jobject object)
 {
+    if (object == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
     int fd = env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
     if (fd >= 0) {
         env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, -1);
@@ -1531,6 +1559,10 @@
 
 static void android_os_Parcel_clearFileDescriptor(JNIEnv* env, jobject clazz, jobject object)
 {
+    if (object == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return;
+    }
     int fd = env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
     if (fd >= 0) {
         env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, -1);
@@ -1735,6 +1767,7 @@
     {"readStrongBinder",    "()Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder},
     {"internalReadFileDescriptor",  "()Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor},
     {"openFileDescriptor",  "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_openFileDescriptor},
+    {"dupFileDescriptor",   "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_dupFileDescriptor},
     {"closeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_closeFileDescriptor},
     {"clearFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_clearFileDescriptor},
     {"freeBuffer",          "()V", (void*)android_os_Parcel_freeBuffer},
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5f471fe..2e72068 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -89,6 +89,7 @@
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IInterface;
 import android.os.IPermissionController;
 import android.os.Looper;
 import android.os.Message;
@@ -1398,35 +1399,7 @@
 
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            ActivityManagerService service = mActivityManagerService;
-            ArrayList<ProcessRecord> procs;
-            synchronized (mActivityManagerService) {
-                if (args != null && args.length > 0
-                        && args[0].charAt(0) != '-') {
-                    procs = new ArrayList<ProcessRecord>();
-                    int pid = -1;
-                    try {
-                        pid = Integer.parseInt(args[0]);
-                    } catch (NumberFormatException e) {
-                        
-                    }
-                    for (int i=service.mLruProcesses.size()-1; i>=0; i--) {
-                        ProcessRecord proc = service.mLruProcesses.get(i);
-                        if (proc.pid == pid) {
-                            procs.add(proc);
-                        } else if (proc.processName.equals(args[0])) {
-                            procs.add(proc);
-                        }
-                    }
-                    if (procs.size() <= 0) {
-                        pw.println("No process found for: " + args[0]);
-                        return;
-                    }
-                } else {
-                    procs = new ArrayList<ProcessRecord>(service.mLruProcesses);
-                }
-            }
-            dumpApplicationMemoryUsage(fd, pw, procs, "  ", args);
+            mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, "  ", args);
         }
     }
 
@@ -1438,35 +1411,7 @@
 
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            ActivityManagerService service = mActivityManagerService;
-            ArrayList<ProcessRecord> procs;
-            synchronized (mActivityManagerService) {
-                if (args != null && args.length > 0
-                        && args[0].charAt(0) != '-') {
-                    procs = new ArrayList<ProcessRecord>();
-                    int pid = -1;
-                    try {
-                        pid = Integer.parseInt(args[0]);
-                    } catch (NumberFormatException e) {
-
-                    }
-                    for (int i=service.mLruProcesses.size()-1; i>=0; i--) {
-                        ProcessRecord proc = service.mLruProcesses.get(i);
-                        if (proc.pid == pid) {
-                            procs.add(proc);
-                        } else if (proc.processName.equals(args[0])) {
-                            procs.add(proc);
-                        }
-                    }
-                    if (procs.size() <= 0) {
-                        pw.println("No process found for: " + args[0]);
-                        return;
-                    }
-                } else {
-                    procs = new ArrayList<ProcessRecord>(service.mLruProcesses);
-                }
-            }
-            dumpGraphicsHardwareUsage(fd, pw, procs);
+            mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args);
         }
     }
 
@@ -7496,6 +7441,7 @@
         }
         
         boolean dumpAll = false;
+        boolean dumpClient = false;
         
         int opti = 0;
         while (opti < args.length) {
@@ -7506,9 +7452,11 @@
             opti++;
             if ("-a".equals(opt)) {
                 dumpAll = true;
+            } else if ("-c".equals(opt)) {
+                dumpClient = true;
             } else if ("-h".equals(opt)) {
                 pw.println("Activity manager dump options:");
-                pw.println("  [-a] [-h] [cmd] ...");
+                pw.println("  [-a] [-c] [-h] [cmd] ...");
                 pw.println("  cmd may be one of:");
                 pw.println("    a[ctivities]: activity stack state");
                 pw.println("    b[roadcasts]: broadcast state");
@@ -7517,10 +7465,14 @@
                 pw.println("    o[om]: out of memory management");
                 pw.println("    prov[iders]: content provider state");
                 pw.println("    s[ervices]: service state");
-                pw.println("    service [name]: service client-side state");
-                pw.println("  cmd may also be a component name (com.foo/.myApp),");
-                pw.println("    a partial substring in a component name, or an");
-                pw.println("    ActivityRecord hex object identifier.");
+                pw.println("    service [COMP_SPEC]: service client-side state");
+                pw.println("  cmd may also be a COMP_SPEC to dump activities.");
+                pw.println("  COMP_SPEC may also be a component name (com.foo/.myApp),");
+                pw.println("    a partial substring in a component name, an");
+                pw.println("    ActivityRecord hex object identifier, or");
+                pw.println("    \"all\" for all objects");
+                pw.println("  -a: include all available server state.");
+                pw.println("  -c: include client state.");
                 return;
             } else {
                 pw.println("Unknown argument: " + opt + "; use -h for help");
@@ -7533,7 +7485,7 @@
             opti++;
             if ("activities".equals(cmd) || "a".equals(cmd)) {
                 synchronized (this) {
-                    dumpActivitiesLocked(fd, pw, args, opti, true, true);
+                    dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient);
                 }
                 return;
             } else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
@@ -7562,20 +7514,33 @@
                 }
                 return;
             } else if ("service".equals(cmd)) {
-                dumpService(fd, pw, args, opti);
+                String[] newArgs;
+                String name;
+                if (opti >= args.length) {
+                    name = null;
+                    newArgs = EMPTY_STRING_ARRAY;
+                } else {
+                    name = args[opti];
+                    opti++;
+                    newArgs = new String[args.length - opti];
+                    if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
+                }
+                if (!dumpService(fd, pw, name, newArgs, 0, dumpAll)) {
+                    pw.println("No services match: " + name);
+                    pw.println("Use -h for help.");
+                }
                 return;
             } else if ("services".equals(cmd) || "s".equals(cmd)) {
                 synchronized (this) {
-                    dumpServicesLocked(fd, pw, args, opti, true);
+                    dumpServicesLocked(fd, pw, args, opti, true, dumpClient);
                 }
                 return;
             } else {
                 // Dumping a single activity?
-                if (dumpActivity(fd, pw, cmd, args, opti, dumpAll)) {
-                    return;
+                if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll)) {
+                    pw.println("Bad activity command, or no activities match: " + cmd);
+                    pw.println("Use -h for help.");
                 }
-                pw.println("Bad activity command, or no activities match: " + cmd);
-                pw.println("Use -h for help.");
                 return;
             }
         }
@@ -7583,16 +7548,12 @@
         // No piece of data specified, dump everything.
         synchronized (this) {
             boolean needSep;
-            if (dumpAll) {
-                pw.println("Providers in Current Activity Manager State:");
-            }
-            needSep = dumpProvidersLocked(fd, pw, args, opti, dumpAll);
+            needSep = dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll);
             if (needSep) {
                 pw.println(" ");
             }
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
-                pw.println("Broadcasts in Current Activity Manager State:");
             }
             needSep = dumpBroadcastsLocked(fd, pw, args, opti, dumpAll);
             if (needSep) {
@@ -7600,88 +7561,95 @@
             }
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
-                pw.println("Services in Current Activity Manager State:");
             }
-            needSep = dumpServicesLocked(fd, pw, args, opti, dumpAll);
+            needSep = dumpProvidersLocked(fd, pw, args, opti, dumpAll);
             if (needSep) {
                 pw.println(" ");
             }
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
-                pw.println("PendingIntents in Current Activity Manager State:");
             }
-            needSep = dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll);
+            needSep = dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient);
             if (needSep) {
                 pw.println(" ");
             }
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
-                pw.println("Activities in Current Activity Manager State:");
             }
-            needSep = dumpActivitiesLocked(fd, pw, args, opti, dumpAll, !dumpAll);
+            needSep = dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient);
             if (needSep) {
                 pw.println(" ");
             }
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
-                pw.println("Processes in Current Activity Manager State:");
             }
             dumpProcessesLocked(fd, pw, args, opti, dumpAll);
         }
     }
     
     boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean dumpAll, boolean needHeader) {
-        if (needHeader) {
-            pw.println("  Activity stack:");
-        }
-        dumpHistoryList(pw, mMainStack.mHistory, "  ", "Hist", true);
+            int opti, boolean dumpAll, boolean dumpClient) {
+        pw.println("ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)");
+        pw.println("  Main stack:");
+        dumpHistoryList(fd, pw, mMainStack.mHistory, "  ", "Hist", true, !dumpAll, dumpClient);
         pw.println(" ");
         pw.println("  Running activities (most recent first):");
-        dumpHistoryList(pw, mMainStack.mLRUActivities, "  ", "Run", false);
+        dumpHistoryList(fd, pw, mMainStack.mLRUActivities, "  ", "Run", false, !dumpAll, false);
         if (mMainStack.mWaitingVisibleActivities.size() > 0) {
             pw.println(" ");
             pw.println("  Activities waiting for another to become visible:");
-            dumpHistoryList(pw, mMainStack.mWaitingVisibleActivities, "  ", "Wait", false);
+            dumpHistoryList(fd, pw, mMainStack.mWaitingVisibleActivities, "  ", "Wait", false,
+                    !dumpAll, false);
         }
         if (mMainStack.mStoppingActivities.size() > 0) {
             pw.println(" ");
             pw.println("  Activities waiting to stop:");
-            dumpHistoryList(pw, mMainStack.mStoppingActivities, "  ", "Stop", false);
+            dumpHistoryList(fd, pw, mMainStack.mStoppingActivities, "  ", "Stop", false,
+                    !dumpAll, false);
         }
         if (mMainStack.mGoingToSleepActivities.size() > 0) {
             pw.println(" ");
             pw.println("  Activities waiting to sleep:");
-            dumpHistoryList(pw, mMainStack.mGoingToSleepActivities, "  ", "Sleep", false);
+            dumpHistoryList(fd, pw, mMainStack.mGoingToSleepActivities, "  ", "Sleep", false,
+                    !dumpAll, false);
         }
         if (mMainStack.mFinishingActivities.size() > 0) {
             pw.println(" ");
             pw.println("  Activities waiting to finish:");
-            dumpHistoryList(pw, mMainStack.mFinishingActivities, "  ", "Fin", false);
+            dumpHistoryList(fd, pw, mMainStack.mFinishingActivities, "  ", "Fin", false,
+                    !dumpAll, false);
         }
 
         pw.println(" ");
-        pw.println("  mPausingActivity: " + mMainStack.mPausingActivity);
+        if (mMainStack.mPausingActivity != null) {
+            pw.println("  mPausingActivity: " + mMainStack.mPausingActivity);
+        }
         pw.println("  mResumedActivity: " + mMainStack.mResumedActivity);
         pw.println("  mFocusedActivity: " + mFocusedActivity);
-        pw.println("  mLastPausedActivity: " + mMainStack.mLastPausedActivity);
-        pw.println("  mSleepTimeout: " + mMainStack.mSleepTimeout);
+        if (dumpAll) {
+            pw.println("  mLastPausedActivity: " + mMainStack.mLastPausedActivity);
+            pw.println("  mSleepTimeout: " + mMainStack.mSleepTimeout);
+        }
 
-        if (dumpAll && mRecentTasks.size() > 0) {
-            pw.println(" ");
-            pw.println("Recent tasks in Current Activity Manager State:");
+        if (mRecentTasks.size() > 0) {
+            pw.println();
+            pw.println("  Recent tasks:");
 
             final int N = mRecentTasks.size();
             for (int i=0; i<N; i++) {
                 TaskRecord tr = mRecentTasks.get(i);
                 pw.print("  * Recent #"); pw.print(i); pw.print(": ");
                         pw.println(tr);
-                mRecentTasks.get(i).dump(pw, "    ");
+                if (dumpAll) {
+                    mRecentTasks.get(i).dump(pw, "    ");
+                }
             }
         }
         
-        pw.println(" ");
-        pw.println("  mCurTask: " + mCurTask);
+        if (dumpAll) {
+            pw.println(" ");
+            pw.println("  mCurTask: " + mCurTask);
+        }
         
         return true;
     }
@@ -7691,6 +7659,8 @@
         boolean needSep = false;
         int numPers = 0;
 
+        pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)");
+
         if (dumpAll) {
             for (SparseArray<ProcessRecord> procs : mProcessNames.getMap().values()) {
                 final int NA = procs.size();
@@ -7714,20 +7684,22 @@
         if (mLruProcesses.size() > 0) {
             if (needSep) pw.println(" ");
             needSep = true;
-            pw.println("  Running processes (most recent first):");
+            pw.println("  Process LRU list (most recent first):");
             dumpProcessOomList(pw, this, mLruProcesses, "    ",
                     "Proc", "PERS", false);
             needSep = true;
         }
 
-        synchronized (mPidsSelfLocked) {
-            if (mPidsSelfLocked.size() > 0) {
-                if (needSep) pw.println(" ");
-                needSep = true;
-                pw.println("  PID mappings:");
-                for (int i=0; i<mPidsSelfLocked.size(); i++) {
-                    pw.print("    PID #"); pw.print(mPidsSelfLocked.keyAt(i));
-                        pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i));
+        if (dumpAll) {
+            synchronized (mPidsSelfLocked) {
+                if (mPidsSelfLocked.size() > 0) {
+                    if (needSep) pw.println(" ");
+                    needSep = true;
+                    pw.println("  PID mappings:");
+                    for (int i=0; i<mPidsSelfLocked.size(); i++) {
+                        pw.print("    PID #"); pw.print(mPidsSelfLocked.keyAt(i));
+                            pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i));
+                    }
                 }
             }
         }
@@ -7812,13 +7784,15 @@
             }
         }
 
-        pw.println(" ");
+        pw.println();
         pw.println("  mHomeProcess: " + mHomeProcess);
         if (mHeavyWeightProcess != null) {
             pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
         }
         pw.println("  mConfiguration: " + mConfiguration);
-        pw.println("  mConfigWillChange: " + mMainStack.mConfigWillChange);
+        if (dumpAll) {
+            pw.println("  mConfigWillChange: " + mMainStack.mConfigWillChange);
+        }
         pw.println("  mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown);
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
                 || mOrigWaitForDebugger) {
@@ -7912,7 +7886,7 @@
 
         needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll);
 
-        pw.println(" ");
+        pw.println();
         pw.println("  mHomeProcess: " + mHomeProcess);
         if (mHeavyWeightProcess != null) {
             pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
@@ -7929,61 +7903,96 @@
      *  - the first arg isn't the flattened component name of an existing service:
      *    dump all services whose component contains the first arg as a substring
      */
-    protected void dumpService(FileDescriptor fd, PrintWriter pw, String[] args, int opti) {
-        String[] newArgs;
-        String componentNameString;
-        ServiceRecord r;
-        if (opti >= args.length) {
-            componentNameString = null;
-            newArgs = EMPTY_STRING_ARRAY;
-            r = null;
-        } else {
-            componentNameString = args[opti];
-            opti++;
-            ComponentName componentName = ComponentName.unflattenFromString(componentNameString);
-            synchronized (this) {
-                r = componentName != null ? mServices.get(componentName) : null;
-            }
-            newArgs = new String[args.length - opti];
-            if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
-        }
+    protected boolean dumpService(FileDescriptor fd, PrintWriter pw, String name, String[] args,
+            int opti, boolean dumpAll) {
+        ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
 
-        if (r != null) {
-            dumpService(fd, pw, r, newArgs);
-        } else {
-            ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
+        if ("all".equals(name)) {
             synchronized (this) {
                 for (ServiceRecord r1 : mServices.values()) {
-                    if (componentNameString == null
-                            || r1.name.flattenToString().contains(componentNameString)) {
+                    services.add(r1);
+                }
+            }
+        } else {
+            ComponentName componentName = name != null
+                    ? ComponentName.unflattenFromString(name) : null;
+            int objectId = 0;
+            if (componentName == null) {
+                // Not a '/' separated full component name; maybe an object ID?
+                try {
+                    objectId = Integer.parseInt(name, 16);
+                    name = null;
+                    componentName = null;
+                } catch (RuntimeException e) {
+                }
+            }
+
+            synchronized (this) {
+                for (ServiceRecord r1 : mServices.values()) {
+                    if (componentName != null) {
+                        if (r1.name.equals(componentName)) {
+                            services.add(r1);
+                        }
+                    } else if (name != null) {
+                        if (r1.name.flattenToString().contains(name)) {
+                            services.add(r1);
+                        }
+                    } else if (System.identityHashCode(r1) == objectId) {
                         services.add(r1);
                     }
                 }
             }
-            for (int i=0; i<services.size(); i++) {
-                dumpService(fd, pw, services.get(i), newArgs);
-            }
         }
+
+        if (services.size() <= 0) {
+            return false;
+        }
+
+        boolean needSep = false;
+        for (int i=0; i<services.size(); i++) {
+            if (needSep) {
+                pw.println();
+            }
+            needSep = true;
+            dumpService("", fd, pw, services.get(i), args, dumpAll);
+        }
+        return true;
     }
 
     /**
      * Invokes IApplicationThread.dumpService() on the thread of the specified service if
      * there is a thread associated with the service.
      */
-    private void dumpService(FileDescriptor fd, PrintWriter pw, ServiceRecord r, String[] args) {
-        pw.println("------------------------------------------------------------"
-                + "-------------------");
-        pw.println("APP SERVICE: " + r.name.flattenToString());
+    private void dumpService(String prefix, FileDescriptor fd, PrintWriter pw,
+            final ServiceRecord r, String[] args, boolean dumpAll) {
+        String innerPrefix = prefix + "  ";
+        synchronized (this) {
+            pw.print(prefix); pw.print("SERVICE ");
+                    pw.print(r.shortName); pw.print(" ");
+                    pw.print(Integer.toHexString(System.identityHashCode(r)));
+                    pw.print(" pid=");
+                    if (r.app != null) pw.println(r.app.pid);
+                    else pw.println("(not running)");
+            if (dumpAll) {
+                r.dump(pw, innerPrefix);
+            }
+        }
         if (r.app != null && r.app.thread != null) {
+            pw.print(prefix); pw.println("  Client:");
+            pw.flush();
             try {
-                // flush anything that is already in the PrintWriter since the thread is going
-                // to write to the file descriptor directly
-                pw.flush();
-                r.app.thread.dumpService(fd, r, args);
-                pw.print("\n");
-                pw.flush();
+                TransferPipe tp = new TransferPipe();
+                try {
+                    r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(), r, args);
+                    tp.setBufferPrefix(prefix + "    ");
+                    tp.go(fd);
+                } finally {
+                    tp.kill();
+                }
+            } catch (IOException e) {
+                pw.println(prefix + "    Failure while dumping the service: " + e);
             } catch (RemoteException e) {
-                pw.println("got a RemoteException while dumping the service");
+                pw.println(prefix + "    Got a RemoteException while dumping the service");
             }
         }
     }
@@ -7997,51 +8006,67 @@
      */
     protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
             int opti, boolean dumpAll) {
-        String[] newArgs;
-        ComponentName componentName = ComponentName.unflattenFromString(name);
-        int objectId = 0;
-        if (componentName == null) {
-            // Not a '/' separated full component name; maybe an object ID?
-            try {
-                objectId = Integer.parseInt(name, 16);
-                name = null;
-                componentName = null;
-            } catch (RuntimeException e) {
-            }
-        }
-        newArgs = new String[args.length - opti];
-        if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
-
         ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
-        synchronized (this) {
-            for (ActivityRecord r1 : (ArrayList<ActivityRecord>)mMainStack.mHistory) {
-                if (componentName != null) {
-                    if (r1.intent.getComponent().equals(componentName)) {
-                        activities.add(r1);
-                    }
-                } else if (name != null) {
-                    if (r1.intent.getComponent().flattenToString().contains(name)) {
-                        activities.add(r1);
-                    }
-                } else if (System.identityHashCode(r1) == objectId) {
+
+        if ("all".equals(name)) {
+            synchronized (this) {
+                for (ActivityRecord r1 : (ArrayList<ActivityRecord>)mMainStack.mHistory) {
                     activities.add(r1);
                 }
             }
+        } else {
+            ComponentName componentName = ComponentName.unflattenFromString(name);
+            int objectId = 0;
+            if (componentName == null) {
+                // Not a '/' separated full component name; maybe an object ID?
+                try {
+                    objectId = Integer.parseInt(name, 16);
+                    name = null;
+                    componentName = null;
+                } catch (RuntimeException e) {
+                }
+            }
+
+            synchronized (this) {
+                for (ActivityRecord r1 : (ArrayList<ActivityRecord>)mMainStack.mHistory) {
+                    if (componentName != null) {
+                        if (r1.intent.getComponent().equals(componentName)) {
+                            activities.add(r1);
+                        }
+                    } else if (name != null) {
+                        if (r1.intent.getComponent().flattenToString().contains(name)) {
+                            activities.add(r1);
+                        }
+                    } else if (System.identityHashCode(r1) == objectId) {
+                        activities.add(r1);
+                    }
+                }
+            }
         }
 
         if (activities.size() <= 0) {
             return false;
         }
 
+        String[] newArgs = new String[args.length - opti];
+        if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
+
         TaskRecord lastTask = null;
+        boolean needSep = false;
         for (int i=activities.size()-1; i>=0; i--) {
             ActivityRecord r = (ActivityRecord)activities.get(i);
-            if (lastTask != r.task) {
-                lastTask = r.task;
-                pw.print("* Task "); pw.print(lastTask.affinity);
-                        pw.print(" id="); pw.println(lastTask.taskId);
-                if (dumpAll) {
-                    lastTask.dump(pw, "  ");
+            if (needSep) {
+                pw.println();
+            }
+            needSep = true;
+            synchronized (this) {
+                if (lastTask != r.task) {
+                    lastTask = r.task;
+                    pw.print("TASK "); pw.print(lastTask.affinity);
+                            pw.print(" id="); pw.println(lastTask.taskId);
+                    if (dumpAll) {
+                        lastTask.dump(pw, "  ");
+                    }
                 }
             }
             dumpActivity("  ", fd, pw, activities.get(i), newArgs, dumpAll);
@@ -8054,26 +8079,35 @@
      * there is a thread associated with the activity.
      */
     private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw,
-            ActivityRecord r, String[] args, boolean dumpAll) {
+            final ActivityRecord r, String[] args, boolean dumpAll) {
+        String innerPrefix = prefix + "  ";
         synchronized (this) {
-            pw.print(prefix); pw.print("* Activity ");
-                    pw.print(Integer.toHexString(System.identityHashCode(r)));
-                    pw.print(" "); pw.print(r.shortComponentName); pw.print(" pid=");
+            pw.print(prefix); pw.print("ACTIVITY "); pw.print(r.shortComponentName);
+                    pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(r)));
+                    pw.print(" pid=");
                     if (r.app != null) pw.println(r.app.pid);
                     else pw.println("(not running)");
             if (dumpAll) {
-                r.dump(pw, prefix + "  ");
+                r.dump(pw, innerPrefix);
             }
         }
         if (r.app != null && r.app.thread != null) {
+            // flush anything that is already in the PrintWriter since the thread is going
+            // to write to the file descriptor directly
+            pw.flush();
             try {
-                // flush anything that is already in the PrintWriter since the thread is going
-                // to write to the file descriptor directly
-                pw.flush();
-                r.app.thread.dumpActivity(fd, r, prefix + "  ", args);
-                pw.flush();
+                TransferPipe tp = new TransferPipe();
+                try {
+                    r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(), r,
+                            innerPrefix, args);
+                    tp.go(fd);
+                } finally {
+                    tp.kill();
+                }
+            } catch (IOException e) {
+                pw.println(innerPrefix + "Failure while dumping the activity: " + e);
             } catch (RemoteException e) {
-                pw.println("got a RemoteException while dumping the activity");
+                pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
             }
         }
     }
@@ -8082,9 +8116,9 @@
             int opti, boolean dumpAll) {
         boolean needSep = false;
         
+        pw.println("ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)");
         if (dumpAll) {
             if (mRegisteredReceivers.size() > 0) {
-                pw.println(" ");
                 pw.println("  Registered Receivers:");
                 Iterator it = mRegisteredReceivers.values().iterator();
                 while (it.hasNext()) {
@@ -8094,16 +8128,16 @@
                 }
             }
     
-            pw.println(" ");
-            pw.println("Receiver Resolver Table:");
-            mReceiverResolver.dump(pw, null, "  ", null, false);
+            pw.println();
+            pw.println("  Receiver Resolver Table:");
+            mReceiverResolver.dump(pw, null, "    ", null, false);
             needSep = true;
         }
         
         if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
                 || mPendingBroadcast != null) {
             if (mParallelBroadcasts.size() > 0) {
-                pw.println(" ");
+                pw.println();
                 pw.println("  Active broadcasts:");
             }
             for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {
@@ -8111,14 +8145,14 @@
                 mParallelBroadcasts.get(i).dump(pw, "    ");
             }
             if (mOrderedBroadcasts.size() > 0) {
-                pw.println(" ");
+                pw.println();
                 pw.println("  Active ordered broadcasts:");
             }
             for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) {
                 pw.println("  Serialized Broadcast #" + i + ":");
                 mOrderedBroadcasts.get(i).dump(pw, "    ");
             }
-            pw.println(" ");
+            pw.println();
             pw.println("  Pending broadcast:");
             if (mPendingBroadcast != null) {
                 mPendingBroadcast.dump(pw, "    ");
@@ -8128,47 +8162,59 @@
             needSep = true;
         }
 
-        if (dumpAll) {
-            pw.println(" ");
-            pw.println("  Historical broadcasts:");
-            for (int i=0; i<MAX_BROADCAST_HISTORY; i++) {
-                BroadcastRecord r = mBroadcastHistory[i];
-                if (r == null) {
+        if (needSep) {
+            pw.println();
+        }
+        pw.println("  Historical broadcasts:");
+        for (int i=0; i<MAX_BROADCAST_HISTORY; i++) {
+            BroadcastRecord r = mBroadcastHistory[i];
+            if (r == null) {
+                break;
+            }
+            if (dumpAll) {
+                pw.print("  Historical Broadcast #"); pw.print(i); pw.println(":");
+                r.dump(pw, "    ");
+            } else {
+                if (i >= 50) {
+                    pw.println("  ...");
                     break;
                 }
-                pw.println("  Historical Broadcast #" + i + ":");
-                r.dump(pw, "    ");
+                pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
             }
-            needSep = true;
         }
+        needSep = true;
         
         if (mStickyBroadcasts != null) {
-            pw.println(" ");
+            pw.println();
             pw.println("  Sticky broadcasts:");
             StringBuilder sb = new StringBuilder(128);
             for (Map.Entry<String, ArrayList<Intent>> ent
                     : mStickyBroadcasts.entrySet()) {
                 pw.print("  * Sticky action "); pw.print(ent.getKey());
-                        pw.println(":");
-                ArrayList<Intent> intents = ent.getValue();
-                final int N = intents.size();
-                for (int i=0; i<N; i++) {
-                    sb.setLength(0);
-                    sb.append("    Intent: ");
-                    intents.get(i).toShortString(sb, true, false);
-                    pw.println(sb.toString());
-                    Bundle bundle = intents.get(i).getExtras();
-                    if (bundle != null) {
-                        pw.print("      ");
-                        pw.println(bundle.toString());
+                if (dumpAll) {
+                    pw.println(":");
+                    ArrayList<Intent> intents = ent.getValue();
+                    final int N = intents.size();
+                    for (int i=0; i<N; i++) {
+                        sb.setLength(0);
+                        sb.append("    Intent: ");
+                        intents.get(i).toShortString(sb, true, false);
+                        pw.println(sb.toString());
+                        Bundle bundle = intents.get(i).getExtras();
+                        if (bundle != null) {
+                            pw.print("      ");
+                            pw.println(bundle.toString());
+                        }
                     }
+                } else {
+                    pw.println("");
                 }
             }
             needSep = true;
         }
         
         if (dumpAll) {
-            pw.println(" ");
+            pw.println();
             pw.println("  mBroadcastsScheduled=" + mBroadcastsScheduled);
             pw.println("  mHandler:");
             mHandler.dump(new PrintWriterPrinter(pw), "    ");
@@ -8179,20 +8225,55 @@
     }
 
     boolean dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean dumpAll) {
+            int opti, boolean dumpAll, boolean dumpClient) {
         boolean needSep = false;
 
-        if (dumpAll) {
-            if (mServices.size() > 0) {
-                pw.println("  Active services:");
-                Iterator<ServiceRecord> it = mServices.values().iterator();
-                while (it.hasNext()) {
-                    ServiceRecord r = it.next();
-                    pw.print("  * "); pw.println(r);
-                    r.dump(pw, "    ");
+        pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
+        if (mServices.size() > 0) {
+            pw.println("  Active services:");
+            long nowReal = SystemClock.elapsedRealtime();
+            Iterator<ServiceRecord> it = mServices.values().iterator();
+            needSep = false;
+            while (it.hasNext()) {
+                ServiceRecord r = it.next();
+                if (needSep) {
+                    pw.println();
                 }
-                needSep = true;
+                pw.print("  * "); pw.println(r);
+                if (dumpAll) {
+                    r.dump(pw, "    ");
+                    needSep = true;
+                } else {
+                    pw.print("    app="); pw.println(r.app);
+                    pw.print("    created=");
+                        TimeUtils.formatDuration(r.createTime, nowReal, pw);
+                        pw.print(" started="); pw.print(r.startRequested);
+                        pw.print(" connections="); pw.println(r.connections.size());
+                }
+                if (dumpClient && r.app != null && r.app.thread != null) {
+                    pw.println("    Client:");
+                    pw.flush();
+                    try {
+                        TransferPipe tp = new TransferPipe();
+                        try {
+                            r.app.thread.dumpService(
+                                    tp.getWriteFd().getFileDescriptor(), r, args);
+                            tp.setBufferPrefix("      ");
+                            // Short timeout, since blocking here can
+                            // deadlock with the application.
+                            tp.go(fd, 2000);
+                        } finally {
+                            tp.kill();
+                        }
+                    } catch (IOException e) {
+                        pw.println("      Failure while dumping the service: " + e);
+                    } catch (RemoteException e) {
+                        pw.println("      Got a RemoteException while dumping the service");
+                    }
+                    needSep = true;
+                }
             }
+            needSep = true;
         }
 
         if (mPendingServices.size() > 0) {
@@ -8252,21 +8333,32 @@
             int opti, boolean dumpAll) {
         boolean needSep = false;
 
-        if (dumpAll) {
-            if (mProvidersByClass.size() > 0) {
-                if (needSep) pw.println(" ");
-                pw.println("  Published content providers (by class):");
-                Iterator<Map.Entry<String, ContentProviderRecord>> it
-                        = mProvidersByClass.entrySet().iterator();
-                while (it.hasNext()) {
-                    Map.Entry<String, ContentProviderRecord> e = it.next();
-                    ContentProviderRecord r = e.getValue();
+        pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)");
+        if (mProvidersByClass.size() > 0) {
+            if (needSep) pw.println(" ");
+            pw.println("  Published content providers (by class):");
+            Iterator<Map.Entry<String, ContentProviderRecord>> it
+                    = mProvidersByClass.entrySet().iterator();
+            while (it.hasNext()) {
+                Map.Entry<String, ContentProviderRecord> e = it.next();
+                ContentProviderRecord r = e.getValue();
+                if (dumpAll) {
                     pw.print("  * "); pw.println(r);
                     r.dump(pw, "    ");
+                } else {
+                    pw.print("  * "); pw.print(r.name.toShortString());
+                    if (r.app != null) {
+                        pw.println(":");
+                        pw.print("      "); pw.println(r.app);
+                    } else {
+                        pw.println();
+                    }
                 }
-                needSep = true;
             }
+            needSep = true;
+        }
     
+        if (dumpAll) {
             if (mProvidersByName.size() > 0) {
                 pw.println(" ");
                 pw.println("  Authority to provider mappings:");
@@ -8303,7 +8395,9 @@
                         pw.println(" holds:");
                 for (UriPermission perm : perms.values()) {
                     pw.print("    "); pw.println(perm);
-                    perm.dump(pw, "      ");
+                    if (dumpAll) {
+                        perm.dump(pw, "      ");
+                    }
                 }
             }
             needSep = true;
@@ -8316,20 +8410,21 @@
             int opti, boolean dumpAll) {
         boolean needSep = false;
         
-        if (dumpAll) {
-            if (this.mIntentSenderRecords.size() > 0) {
-                Iterator<WeakReference<PendingIntentRecord>> it
-                        = mIntentSenderRecords.values().iterator();
-                while (it.hasNext()) {
-                    WeakReference<PendingIntentRecord> ref = it.next();
-                    PendingIntentRecord rec = ref != null ? ref.get(): null;
-                    needSep = true;
-                    if (rec != null) {
-                        pw.print("  * "); pw.println(rec);
+        if (this.mIntentSenderRecords.size() > 0) {
+            pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)");
+            Iterator<WeakReference<PendingIntentRecord>> it
+                    = mIntentSenderRecords.values().iterator();
+            while (it.hasNext()) {
+                WeakReference<PendingIntentRecord> ref = it.next();
+                PendingIntentRecord rec = ref != null ? ref.get(): null;
+                needSep = true;
+                if (rec != null) {
+                    pw.print("  * "); pw.println(rec);
+                    if (dumpAll) {
                         rec.dump(pw, "    ");
-                    } else {
-                        pw.print("  * "); pw.print(ref);
                     }
+                } else {
+                    pw.print("  * "); pw.println(ref);
                 }
             }
         }
@@ -8337,12 +8432,19 @@
         return needSep;
     }
 
-    private static final void dumpHistoryList(PrintWriter pw, List list,
-            String prefix, String label, boolean complete) {
+    private static final void dumpHistoryList(FileDescriptor fd, PrintWriter pw, List list,
+            String prefix, String label, boolean complete, boolean brief, boolean client) {
         TaskRecord lastTask = null;
+        boolean needNL = false;
+        final String innerPrefix = prefix + "      ";
+        final String[] args = new String[0];
         for (int i=list.size()-1; i>=0; i--) {
-            ActivityRecord r = (ActivityRecord)list.get(i);
-            final boolean full = complete || !r.inHistory;
+            final ActivityRecord r = (ActivityRecord)list.get(i);
+            final boolean full = !brief && (complete || !r.inHistory);
+            if (needNL) {
+                pw.println(" ");
+                needNL = false;
+            }
             if (lastTask != r.task) {
                 lastTask = r.task;
                 pw.print(prefix);
@@ -8350,13 +8452,46 @@
                 pw.println(lastTask);
                 if (full) {
                     lastTask.dump(pw, prefix + "  ");
+                } else if (complete) {
+                    // Complete + brief == give a summary.  Isn't that obvious?!?
+                    if (lastTask.intent != null) {
+                        pw.print(prefix); pw.print("  "); pw.println(lastTask.intent);
+                    }
                 }
             }
             pw.print(prefix); pw.print(full ? "  * " : "    "); pw.print(label);
             pw.print(" #"); pw.print(i); pw.print(": ");
             pw.println(r);
             if (full) {
-                r.dump(pw, prefix + "      ");
+                r.dump(pw, innerPrefix);
+            } else if (complete) {
+                // Complete + brief == give a summary.  Isn't that obvious?!?
+                pw.print(innerPrefix); pw.println(r.intent);
+                if (r.app != null) {
+                    pw.print(innerPrefix); pw.println(r.app);
+                }
+            }
+            if (client && r.app != null && r.app.thread != null) {
+                // flush anything that is already in the PrintWriter since the thread is going
+                // to write to the file descriptor directly
+                pw.flush();
+                try {
+                    TransferPipe tp = new TransferPipe();
+                    try {
+                        r.app.thread.dumpActivity(tp.getWriteFd().getFileDescriptor(), r,
+                                innerPrefix, args);
+                        // Short timeout, since blocking here can
+                        // deadlock with the application.
+                        tp.go(fd, 2000);
+                    } finally {
+                        tp.kill();
+                    }
+                } catch (IOException e) {
+                    pw.println(innerPrefix + "Failure while dumping the activity: " + e);
+                } catch (RemoteException e) {
+                    pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
+                }
+                needNL = true;
             }
         }
     }
@@ -8442,7 +8577,7 @@
                     N-i, oomAdj, schedGroup, r.toShortString(), r.adjType));
             if (r.adjSource != null || r.adjTarget != null) {
                 pw.print(prefix);
-                pw.print("          ");
+                pw.print("    ");
                 if (r.adjTarget instanceof ComponentName) {
                     pw.print(((ComponentName)r.adjTarget).flattenToShortString());
                 } else if (r.adjTarget != null) {
@@ -8512,30 +8647,75 @@
         }
     }
 
-    static final void dumpGraphicsHardwareUsage(FileDescriptor fd,
-            PrintWriter pw, List list) {
-        String args[] = {"graphics"};
-        pw.println("-------------------------------------------------------------------------------");
-        pw.println("DUMP OF GRAPHICS ACCELERATION INFO:");
-        for (int i = list.size() - 1 ; i >= 0 ; i--) {
-            ProcessRecord r = (ProcessRecord)list.get(i);
+    ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, String[] args) {
+        ArrayList<ProcessRecord> procs;
+        synchronized (this) {
+            if (args != null && args.length > 0
+                    && args[0].charAt(0) != '-') {
+                procs = new ArrayList<ProcessRecord>();
+                int pid = -1;
+                try {
+                    pid = Integer.parseInt(args[0]);
+                } catch (NumberFormatException e) {
+
+                }
+                for (int i=mLruProcesses.size()-1; i>=0; i--) {
+                    ProcessRecord proc = mLruProcesses.get(i);
+                    if (proc.pid == pid) {
+                        procs.add(proc);
+                    } else if (proc.processName.equals(args[0])) {
+                        procs.add(proc);
+                    }
+                }
+                if (procs.size() <= 0) {
+                    pw.println("No process found for: " + args[0]);
+                    return null;
+                }
+            } else {
+                procs = new ArrayList<ProcessRecord>(mLruProcesses);
+            }
+        }
+        return procs;
+    }
+
+    final void dumpGraphicsHardwareUsage(FileDescriptor fd,
+            PrintWriter pw, String[] args) {
+        ArrayList<ProcessRecord> procs = collectProcesses(pw, args);
+        if (procs == null) {
+            return;
+        }
+
+        long uptime = SystemClock.uptimeMillis();
+        long realtime = SystemClock.elapsedRealtime();
+        pw.println("Applications Graphics Acceleration Info:");
+        pw.println("Uptime: " + uptime + " Realtime: " + realtime);
+        
+        String callArgs[] = {"graphics"};
+        for (int i = procs.size() - 1 ; i >= 0 ; i--) {
+            ProcessRecord r = procs.get(i);
             if (r.thread != null) {
                 pw.println("\n** Graphics info for pid " + r.pid + " [" + r.processName + "] **");
                 pw.flush();
                 try {
-                    r.thread.asBinder().dump(fd, args);
+                    TransferPipe.goDump(r.thread.asBinder(), fd, callArgs);
+                } catch (IOException e) {
+                    pw.println("Failure: " + e);
+                    pw.flush();
                 } catch (RemoteException e) {
                     pw.println("Got RemoteException!");
                     pw.flush();
                 }
             }
         }
-        pw.println("\n");
-        pw.flush();
     }
 
-    static final void dumpApplicationMemoryUsage(FileDescriptor fd,
-            PrintWriter pw, List list, String prefix, String[] args) {
+    final void dumpApplicationMemoryUsage(FileDescriptor fd,
+            PrintWriter pw, String prefix, String[] args) {
+        ArrayList<ProcessRecord> procs = collectProcesses(pw, args);
+        if (procs == null) {
+            return;
+        }
+
         final boolean isCheckinRequest = scanArgs(args, "--checkin");
         long uptime = SystemClock.uptimeMillis();
         long realtime = SystemClock.elapsedRealtime();
@@ -8548,15 +8728,18 @@
             pw.println("Applications Memory Usage (kB):");
             pw.println("Uptime: " + uptime + " Realtime: " + realtime);
         }
-        for (int i = list.size() - 1 ; i >= 0 ; i--) {
-            ProcessRecord r = (ProcessRecord)list.get(i);
+        for (int i = procs.size() - 1 ; i >= 0 ; i--) {
+            ProcessRecord r = procs.get(i);
             if (r.thread != null) {
                 if (!isCheckinRequest) {
                     pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **");
                     pw.flush();
                 }
                 try {
-                    r.thread.asBinder().dump(fd, args);
+                    TransferPipe.goDump(r.thread.asBinder(), fd, args);
+                } catch (IOException e) {
+                    pw.println("Failure: " + e);
+                    pw.flush();
                 } catch (RemoteException e) {
                     if (!isCheckinRequest) {
                         pw.println("Got RemoteException!");
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 0fb30ff..8d8f303 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -135,7 +135,7 @@
         pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
                 pw.print(" componentSpecified="); pw.print(componentSpecified);
                 pw.print(" isHomeActivity="); pw.println(isHomeActivity);
-        pw.print(prefix); pw.print("configuration="); pw.println(configuration);
+        pw.print(prefix); pw.print("config="); pw.println(configuration);
         if (resultTo != null || resultWho != null) {
             pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
                     pw.print(" resultWho="); pw.print(resultWho);
diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java
index 44c9742..db235ee 100644
--- a/services/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/java/com/android/server/am/ContentProviderRecord.java
@@ -60,7 +60,7 @@
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("package=");
                 pw.print(info.applicationInfo.packageName);
-                pw.print("process="); pw.println(info.processName);
+                pw.print(" process="); pw.println(info.processName);
         pw.print(prefix); pw.print("app="); pw.println(app);
         if (launchingApp != null) {
             pw.print(prefix); pw.print("launchingApp="); pw.println(launchingApp);
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index e5aceb4..1a617dd 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -211,7 +211,7 @@
                 pw.print(" lastActivity=");
                 TimeUtils.formatDuration(lastActivity, now, pw);
                 pw.println("");
-        pw.print(prefix); pw.print(" executingStart=");
+        pw.print(prefix); pw.print("executingStart=");
                 TimeUtils.formatDuration(executingStart, now, pw);
                 pw.print(" restartTime=");
                 TimeUtils.formatDuration(restartTime, now, pw);
diff --git a/services/java/com/android/server/am/TransferPipe.java b/services/java/com/android/server/am/TransferPipe.java
new file mode 100644
index 0000000..c3f4f93
--- /dev/null
+++ b/services/java/com/android/server/am/TransferPipe.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2011 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.am;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Slog;
+
+/**
+ * Helper for transferring data through a pipe from a client app.
+ */
+class TransferPipe implements Runnable {
+    static final String TAG = "TransferPipe";
+    static final boolean DEBUG = false;
+
+    static final long DEFAULT_TIMEOUT = 5000;  // 5 seconds
+
+    final Thread mThread;;
+    final ParcelFileDescriptor[] mFds;
+
+    FileDescriptor mOutFd;
+    long mEndTime;
+    String mFailure;
+    boolean mComplete;
+
+    String mBufferPrefix;
+
+    interface Caller {
+        void go(IInterface iface, FileDescriptor fd, String prefix,
+                String[] args) throws RemoteException;
+    }
+
+    TransferPipe() throws IOException {
+        mThread = new Thread(this, "TransferPipe");
+        mFds = ParcelFileDescriptor.createPipe();
+    }
+
+    ParcelFileDescriptor getReadFd() {
+        return mFds[0];
+    }
+
+    ParcelFileDescriptor getWriteFd() {
+        return mFds[1];
+    }
+
+    void setBufferPrefix(String prefix) {
+        mBufferPrefix = prefix;
+    }
+
+    static void go(Caller caller, IInterface iface, FileDescriptor out,
+            String prefix, String[] args) throws IOException, RemoteException {
+        go(caller, iface, out, prefix, args, DEFAULT_TIMEOUT);
+    }
+
+    static void go(Caller caller, IInterface iface, FileDescriptor out,
+            String prefix, String[] args, long timeout) throws IOException, RemoteException {
+        if ((iface.asBinder()) instanceof Binder) {
+            // This is a local object...  just call it directly.
+            try {
+                caller.go(iface, out, prefix, args);
+            } catch (RemoteException e) {
+            }
+            return;
+        }
+
+        TransferPipe tp = new TransferPipe();
+        try {
+            caller.go(iface, tp.getWriteFd().getFileDescriptor(), prefix, args);
+            tp.go(out, timeout);
+        } finally {
+            tp.kill();
+        }
+    }
+
+    static void goDump(IBinder binder, FileDescriptor out,
+            String[] args) throws IOException, RemoteException {
+        goDump(binder, out, args, DEFAULT_TIMEOUT);
+    }
+
+    static void goDump(IBinder binder, FileDescriptor out,
+            String[] args, long timeout) throws IOException, RemoteException {
+        if (binder instanceof Binder) {
+            // This is a local object...  just call it directly.
+            try {
+                binder.dump(out, args);
+            } catch (RemoteException e) {
+            }
+            return;
+        }
+
+        TransferPipe tp = new TransferPipe();
+        try {
+            binder.dumpAsync(tp.getWriteFd().getFileDescriptor(), args);
+            tp.go(out, timeout);
+        } finally {
+            tp.kill();
+        }
+    }
+
+    void go(FileDescriptor out) throws IOException {
+        go(out, DEFAULT_TIMEOUT);
+    }
+
+    void go(FileDescriptor out, long timeout) throws IOException {
+        try {
+            synchronized (this) {
+                mOutFd = out;
+                mEndTime = SystemClock.uptimeMillis() + timeout;
+
+                if (DEBUG) Slog.i(TAG, "read=" + getReadFd() + " write=" + getWriteFd()
+                        + " out=" + out);
+
+                // Close the write fd, so we know when the other side is done.
+                closeFd(1);
+
+                mThread.start();
+
+                while (mFailure == null && !mComplete) {
+                    long waitTime = mEndTime - SystemClock.uptimeMillis();
+                    if (waitTime <= 0) {
+                        if (DEBUG) Slog.i(TAG, "TIMEOUT!");
+                        mThread.interrupt();
+                        throw new IOException("Timeout");
+                    }
+
+                    try {
+                        wait(waitTime);
+                    } catch (InterruptedException e) {
+                    }
+                }
+
+                if (DEBUG) Slog.i(TAG, "Finished: " + mFailure);
+                if (mFailure != null) {
+                    throw new IOException(mFailure);
+                }
+            }
+        } finally {
+            kill();
+        }
+    }
+
+    void closeFd(int num) {
+        if (mFds[num] != null) {
+            if (DEBUG) Slog.i(TAG, "Closing: " + mFds[num]);
+            try {
+                mFds[num].close();
+            } catch (IOException e) {
+            }
+            mFds[num] = null;
+        }
+    }
+
+    void kill() {
+        closeFd(0);
+        closeFd(1);
+    }
+
+    @Override
+    public void run() {
+        final byte[] buffer = new byte[1024];
+        final FileInputStream fis = new FileInputStream(getReadFd().getFileDescriptor());
+        final FileOutputStream fos = new FileOutputStream(mOutFd);
+
+        if (DEBUG) Slog.i(TAG, "Ready to read pipe...");
+        byte[] bufferPrefix = null;
+        boolean needPrefix = true;
+        if (mBufferPrefix != null) {
+            bufferPrefix = mBufferPrefix.getBytes();
+        }
+
+        int size;
+        try {
+            while ((size=fis.read(buffer)) > 0) {
+                if (DEBUG) Slog.i(TAG, "Got " + size + " bytes");
+                if (bufferPrefix == null) {
+                    fos.write(buffer, 0, size);
+                } else {
+                    int start = 0;
+                    for (int i=0; i<size; i++) {
+                        if (buffer[i] != '\n') {
+                            if (i > start) {
+                                fos.write(buffer, start, i-start);
+                            }
+                            start = i;
+                            if (needPrefix) {
+                                fos.write(bufferPrefix);
+                                needPrefix = false;
+                            }
+                            do {
+                                i++;
+                            } while (i<size && buffer[i] != '\n');
+                            if (i < size) {
+                                needPrefix = true;
+                            }
+                        }
+                    }
+                    if (size > start) {
+                        fos.write(buffer, start, size-start);
+                    }
+                }
+            }
+            if (DEBUG) Slog.i(TAG, "End of pipe: size=" + size);
+            if (mThread.isInterrupted()) {
+                if (DEBUG) Slog.i(TAG, "Interrupted!");
+            }
+        } catch (IOException e) {
+            synchronized (this) {
+                mFailure = e.toString();
+                notifyAll();
+                return;
+            }
+        }
+
+        synchronized (this) {
+            mComplete = true;
+            notifyAll();
+        }
+    }
+}