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/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();
+        }
+    }
+}