diff options
author | Eric Miao <ericymiao@google.com> | 2024-02-06 10:28:44 -0800 |
---|---|---|
committer | Eric Miao <ericymiao@google.com> | 2024-04-02 11:35:43 -0700 |
commit | d48d873a2f6dfdcb3962b08bfa025ddb398bb57d (patch) | |
tree | c072ee30e10f9ba053b1e9127503275e2ae220cd /graphics | |
parent | 9a885d7439b4dff37f15e562fb1b15e959f76caf (diff) | |
download | base-d48d873a2f6dfdcb3962b08bfa025ddb398bb57d.tar.gz |
Add compressed bitmaps to be included in `am dumpheap`
Bug: 328443220
`android.graphics.Bitmap` class used to have a field of byte array
`mBuffer` for its content. This allowed the bitmap content to be
included as part of `am dumpheap`. However, this field was removed
when Bitmap was migrated to use native memory.
This CL allows contents of bitmaps to be compressed and included as
part of `am dumpheap`, with added command line switch '-b <format>'.
For example, the command below will include the contents of the
bitmaps compressed in PNG format as part of the heap dump.
`am dumpheap -b png com.google.android.apps.photos`
This is done with a few key changes below:
1. Every bitmap instance created will be tracked by a static
`WeakHashMap`. This is so that 1) the bitmap instances are
used as weak keys and can be garbage collected normally,
and 2) when a bitmap instance is garbage collected, its
entry in `WeakHashMap` will also be removed, so the size
of the map itself is limited
2. A static field `Bitmap.dumpData` is introduced, and will
record every bitmap's `nativePtr` and its compressed
content when bitmap dump is enabled during a heap dump
3. `Bitmap.dumpData` will be cleared after the heap is dumped,
the recorded information as well as buffers with compressed
contents will be garbage collected thereafter.
Change-Id: I37b6ea6b947565d1ac5a6bbc5b462c3ceedebec1
Diffstat (limited to 'graphics')
-rw-r--r-- | graphics/java/android/graphics/Bitmap.java | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 250362b1e1e3..319f115d7427 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -41,12 +41,15 @@ import dalvik.annotation.optimization.CriticalNative; import libcore.util.NativeAllocationRegistry; import java.io.IOException; +import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.lang.ref.WeakReference; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; +import java.util.ArrayList; +import java.util.WeakHashMap; public final class Bitmap implements Parcelable { private static final String TAG = "Bitmap"; @@ -120,6 +123,11 @@ public final class Bitmap implements Parcelable { } /** + * @hide + */ + private static final WeakHashMap<Bitmap, Void> sAllBitmaps = new WeakHashMap<>(); + + /** * Private constructor that must receive an already allocated native bitmap * int (pointer). */ @@ -162,6 +170,9 @@ public final class Bitmap implements Parcelable { Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), allocationByteCount); } registry.registerNativeAllocation(this, nativeBitmap); + synchronized (Bitmap.class) { + sAllBitmaps.put(this, null); + } } /** @@ -1510,6 +1521,86 @@ public final class Bitmap implements Parcelable { } /** + * @hide + */ + private static final class DumpData { + private int count; + private int format; + private long[] natives; + private byte[][] buffers; + private int max; + + public DumpData(@NonNull CompressFormat format, int max) { + this.max = max; + this.format = format.nativeInt; + this.natives = new long[max]; + this.buffers = new byte[max][]; + this.count = 0; + } + + public void add(long nativePtr, byte[] buffer) { + natives[count] = nativePtr; + buffers[count] = buffer; + count = (count >= max) ? max : count + 1; + } + + public int size() { + return count; + } + } + + /** + * @hide + */ + private static DumpData dumpData = null; + + + /** + * @hide + * + * Dump all the bitmaps with their contents compressed into dumpData + * + * @param format format of the compressed image, null to clear dump data + */ + public static void dumpAll(@Nullable String format) { + if (format == null) { + /* release the dump data */ + dumpData = null; + return; + } + final CompressFormat fmt; + if (format.equals("jpg") || format.equals("jpeg")) { + fmt = CompressFormat.JPEG; + } else if (format.equals("png")) { + fmt = CompressFormat.PNG; + } else if (format.equals("webp")) { + fmt = CompressFormat.WEBP_LOSSLESS; + } else { + Log.w(TAG, "No bitmaps dumped: unrecognized format " + format); + return; + } + + final ArrayList<Bitmap> allBitmaps; + synchronized (Bitmap.class) { + allBitmaps = new ArrayList<>(sAllBitmaps.size()); + for (Bitmap bitmap : sAllBitmaps.keySet()) { + if (bitmap != null && !bitmap.isRecycled()) { + allBitmaps.add(bitmap); + } + } + } + + dumpData = new DumpData(fmt, allBitmaps.size()); + for (Bitmap bitmap : allBitmaps) { + ByteArrayOutputStream bas = new ByteArrayOutputStream(); + if (bitmap.compress(fmt, 90, bas)) { + dumpData.add(bitmap.getNativeInstance(), bas.toByteArray()); + } + } + Log.i(TAG, dumpData.size() + "/" + allBitmaps.size() + " bitmaps dumped"); + } + + /** * Number of bytes of temp storage we use for communicating between the * native compressor and the java OutputStream. */ |