Merge "Avoid ANRs in dumpCacheInfo" into tm-dev am: 8dc0bab75e am: 50ee494d30 am: c8d7b7c691

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18717283

Change-Id: I98f8bb60ba63510fa4fe71402542badc5c5cea8b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index b49e571..58ddd49 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -33,6 +33,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.io.ByteArrayOutputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -1444,7 +1445,6 @@
                     mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow));
             pw.println(TextUtils.formatSimple("    Enabled: %s", mDisabled ? "false" : "true"));
             pw.println("");
-            pw.flush();
 
             // No specific cache was requested.  This is the default, and no details
             // should be dumped.
@@ -1463,7 +1463,6 @@
 
                 pw.println(TextUtils.formatSimple("      Key: %s\n      Value: %s\n", key, value));
             }
-            pw.flush();
         }
     }
 
@@ -1488,34 +1487,54 @@
      * provided ParcelFileDescriptor.  Optional switches allow the caller to choose
      * specific caches (selection is by cache name or property name); if these switches
      * are used then the output includes both cache statistics and cache entries.
+     */
+    private static void dumpCacheInfo(@NonNull PrintWriter pw, @NonNull String[] args) {
+        if (!sEnabled) {
+            pw.println("  Caching is disabled in this process.");
+            return;
+        }
+
+        // See if detailed is requested for any cache.  If there is a specific detailed request,
+        // then only that cache is reported.
+        boolean detail = anyDetailed(args);
+
+        ArrayList<PropertyInvalidatedCache> activeCaches;
+        synchronized (sCorkLock) {
+            activeCaches = getActiveCaches();
+            if (!detail) {
+                dumpCorkInfo(pw);
+            }
+        }
+
+        for (int i = 0; i < activeCaches.size(); i++) {
+            PropertyInvalidatedCache currentCache = activeCaches.get(i);
+            currentCache.dumpContents(pw, detail, args);
+        }
+    }
+
+    /**
+     * Without arguments, this dumps statistics from every cache in the process to the
+     * provided ParcelFileDescriptor.  Optional switches allow the caller to choose
+     * specific caches (selection is by cache name or property name); if these switches
+     * are used then the output includes both cache statistics and cache entries.
      * @hide
      */
     public static void dumpCacheInfo(@NonNull ParcelFileDescriptor pfd, @NonNull String[] args) {
-        try  (
-            FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor());
-            PrintWriter pw = new FastPrintWriter(fout);
-        ) {
-            if (!sEnabled) {
-                pw.println("  Caching is disabled in this process.");
-                return;
-            }
+        // Create a PrintWriter that uses a byte array.  The code can safely write to
+        // this array without fear of blocking.  The completed byte array will be sent
+        // to the caller after all the data has been collected and all locks have been
+        // released.
+        ByteArrayOutputStream barray = new ByteArrayOutputStream();
+        PrintWriter bout = new PrintWriter(barray);
+        dumpCacheInfo(bout, args);
+        bout.close();
 
-            // See if detailed is requested for any cache.  If there is a specific detailed request,
-            // then only that cache is reported.
-            boolean detail = anyDetailed(args);
-
-            ArrayList<PropertyInvalidatedCache> activeCaches;
-            synchronized (sCorkLock) {
-                activeCaches = getActiveCaches();
-                if (!detail) {
-                    dumpCorkInfo(pw);
-                }
-            }
-
-            for (int i = 0; i < activeCaches.size(); i++) {
-                PropertyInvalidatedCache currentCache = activeCaches.get(i);
-                currentCache.dumpContents(pw, detail, args);
-            }
+        try {
+            // Send the final byte array to the output.  This happens outside of all locks.
+            var out = new FileOutputStream(pfd.getFileDescriptor());
+            barray.writeTo(out);
+            out.close();
+            barray.close();
         } catch (IOException e) {
             Log.e(TAG, "Failed to dump PropertyInvalidatedCache instances");
         }