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/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},