Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 51e7e1d1 authored by Automerger Merge Worker's avatar Automerger Merge Worker Committed by Android (Google) Code Review
Browse files

Merge "Merge "Describe image loads in Traces" into tm-qpr-dev am: bb5c8100 am: 083e6141"

parents 14229765 ddb2e4b6
Loading
Loading
Loading
Loading
+206 −57
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Trace;
import android.system.ErrnoException;
import android.system.Os;
import android.util.DisplayMetrics;
@@ -223,13 +224,21 @@ public final class ImageDecoder implements AutoCloseable {
        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
            return nCreate(mData, mOffset, mLength, preferAnimation, this);
        }

        @Override
        public String toString() {
            return "ByteArraySource{len=" + mLength + "}";
        }
    }

    private static class ByteBufferSource extends Source {
        ByteBufferSource(@NonNull ByteBuffer buffer) {
            mBuffer = buffer;
            mLength = mBuffer.limit() - mBuffer.position();
        }

        private final ByteBuffer mBuffer;
        private final int mLength;

        @Override
        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
@@ -241,6 +250,11 @@ public final class ImageDecoder implements AutoCloseable {
            ByteBuffer buffer = mBuffer.slice();
            return nCreate(buffer, buffer.position(), buffer.limit(), preferAnimation, this);
        }

        @Override
        public String toString() {
            return "ByteBufferSource{len=" + mLength + "}";
        }
    }

    private static class ContentResolverSource extends Source {
@@ -285,6 +299,16 @@ public final class ImageDecoder implements AutoCloseable {

            return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
        }

        @Override
        public String toString() {
            String uri = mUri.toString();
            if (uri.length() > 90) {
                // We want to keep the Uri usable - usually the authority and the end is important.
                uri = uri.substring(0, 80) + ".." + uri.substring(uri.length() - 10);
            }
            return "ContentResolverSource{uri=" + uri + "}";
        }
    }

    @NonNull
@@ -399,6 +423,11 @@ public final class ImageDecoder implements AutoCloseable {
                return createFromStream(is, false, preferAnimation, this);
            }
        }

        @Override
        public String toString() {
            return "InputStream{s=" + mInputStream + "}";
        }
    }

    /**
@@ -444,6 +473,11 @@ public final class ImageDecoder implements AutoCloseable {
                return createFromAsset(ais, preferAnimation, this);
            }
        }

        @Override
        public String toString() {
            return "AssetInputStream{s=" + mAssetInputStream + "}";
        }
    }

    private static class ResourceSource extends Source {
@@ -485,6 +519,17 @@ public final class ImageDecoder implements AutoCloseable {

            return createFromAsset((AssetInputStream) is, preferAnimation, this);
        }

        @Override
        public String toString() {
            // Try to return a human-readable name for debugging purposes.
            try {
                return "Resource{name=" + mResources.getResourceName(mResId) + "}";
            } catch (Resources.NotFoundException e) {
                // It's ok if we don't find it, fall back to ID.
            }
            return "Resource{id=" + mResId + "}";
        }
    }

    /**
@@ -521,6 +566,11 @@ public final class ImageDecoder implements AutoCloseable {
            InputStream is = mAssets.open(mFileName);
            return createFromAsset((AssetInputStream) is, preferAnimation, this);
        }

        @Override
        public String toString() {
            return "AssetSource{file=" + mFileName + "}";
        }
    }

    private static class FileSource extends Source {
@@ -534,6 +584,11 @@ public final class ImageDecoder implements AutoCloseable {
        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
            return createFromFile(mFile, preferAnimation, this);
        }

        @Override
        public String toString() {
            return "FileSource{file=" + mFile + "}";
        }
    }

    private static class CallableSource extends Source {
@@ -557,6 +612,11 @@ public final class ImageDecoder implements AutoCloseable {
            }
            return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
        }

        @Override
        public String toString() {
            return "CallableSource{obj=" + mCallable.toString() + "}";
        }
    }

    /**
@@ -1763,20 +1823,21 @@ public final class ImageDecoder implements AutoCloseable {
    @NonNull
    private static Drawable decodeDrawableImpl(@NonNull Source src,
            @Nullable OnHeaderDecodedListener listener) throws IOException {
        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ImageDecoder#decodeDrawable");
        try (ImageDecoder decoder = src.createImageDecoder(true /*preferAnimation*/)) {
            decoder.mSource = src;
            decoder.callHeaderDecoded(listener, src);

            try (ImageDecoderSourceTrace unused = new ImageDecoderSourceTrace(decoder)) {
                if (decoder.mUnpremultipliedRequired) {
                    // Though this could be supported (ignored) for opaque images,
                    // it seems better to always report this error.
                throw new IllegalStateException("Cannot decode a Drawable " +
                                                "with unpremultiplied pixels!");
                    throw new IllegalStateException(
                            "Cannot decode a Drawable with unpremultiplied pixels!");
                }

                if (decoder.mMutable) {
                throw new IllegalStateException("Cannot decode a mutable " +
                                                "Drawable!");
                    throw new IllegalStateException("Cannot decode a mutable Drawable!");
                }

                // this call potentially manipulates the decoder so it must be performed prior to
@@ -1785,8 +1846,7 @@ public final class ImageDecoder implements AutoCloseable {
                if (decoder.mAnimated) {
                    // AnimatedImageDrawable calls postProcessAndRelease only if
                    // mPostProcessor exists.
                ImageDecoder postProcessPtr = decoder.mPostProcessor == null ?
                        null : decoder;
                    ImageDecoder postProcessPtr = decoder.mPostProcessor == null ? null : decoder;
                    decoder.checkState(true);
                    Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
                            postProcessPtr, decoder.mDesiredWidth,
@@ -1819,6 +1879,10 @@ public final class ImageDecoder implements AutoCloseable {

                return new BitmapDrawable(res, bm);
            }
        } finally {
            // Close the ImageDecoder#decode trace.
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        }
    }

    /**
@@ -1867,10 +1931,11 @@ public final class ImageDecoder implements AutoCloseable {
    @NonNull
    private static Bitmap decodeBitmapImpl(@NonNull Source src,
            @Nullable OnHeaderDecodedListener listener) throws IOException {
        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ImageDecoder#decodeBitmap");
        try (ImageDecoder decoder = src.createImageDecoder(false /*preferAnimation*/)) {
            decoder.mSource = src;
            decoder.callHeaderDecoded(listener, src);

            try (ImageDecoderSourceTrace unused = new ImageDecoderSourceTrace(decoder)) {
                // this call potentially manipulates the decoder so it must be performed prior to
                // decoding the bitmap
                final int srcDensity = decoder.computeDensity(src);
@@ -1884,9 +1949,33 @@ public final class ImageDecoder implements AutoCloseable {
                        nGetPadding(decoder.mNativePtr, padding);
                    }
                }

                return bm;
            }
        } finally {
            // Close the ImageDecoder#decode trace.
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        }
    }

    /**
     * This describes the decoder in traces to ease debugging. It has to be called after
     * header has been decoded and width/height have been populated. It should be used
     * inside a try-with-resources call to automatically complete the trace.
     */
    private static AutoCloseable traceDecoderSource(ImageDecoder decoder) {
        final boolean resourceTracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_RESOURCES);
        if (resourceTracingEnabled) {
            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, describeDecoderForTrace(decoder));
        }

        return new AutoCloseable() {
            @Override
            public void close() throws Exception {
                if (resourceTracingEnabled) {
                    Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
                }
            }
        };
    }

    // This method may modify the decoder so it must be called prior to performing the decode
@@ -1994,6 +2083,66 @@ public final class ImageDecoder implements AutoCloseable {
        }
    }

    /**
     * Returns a short string describing what passed ImageDecoder is loading -
     * it reports image dimensions, desired dimensions (if any) and source resource.
     *
     * The string appears in perf traces to simplify search for slow or memory intensive
     * image loads.
     *
     * Example: ID#w=300;h=250;dw=150;dh=150;src=Resource{name=@resource}
     *
     * @hide
     */
    private static String describeDecoderForTrace(@NonNull ImageDecoder decoder) {
        StringBuilder builder = new StringBuilder();
        // Source dimensions
        builder.append("ID#w=");
        builder.append(decoder.mWidth);
        builder.append(";h=");
        builder.append(decoder.mHeight);
        // Desired dimensions (if present)
        if (decoder.mDesiredWidth != decoder.mWidth
                || decoder.mDesiredHeight != decoder.mHeight) {
            builder.append(";dw=");
            builder.append(decoder.mDesiredWidth);
            builder.append(";dh=");
            builder.append(decoder.mDesiredHeight);
        }
        // Source description
        builder.append(";src=");
        builder.append(decoder.mSource);
        return builder.toString();
    }

    /**
     * Records a trace with information about the source being decoded - dimensions,
     * desired dimensions and source information.
     *
     * It significantly eases debugging of slow resource loads on main thread and
     * possible large memory consumers.
     *
     * @hide
     */
    private static final class ImageDecoderSourceTrace implements AutoCloseable {

        private final boolean mResourceTracingEnabled;

        ImageDecoderSourceTrace(ImageDecoder decoder) {
            mResourceTracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_RESOURCES);
            if (mResourceTracingEnabled) {
                Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, describeDecoderForTrace(decoder));
            }
        }

        @Override
        public void close() {
            if (mResourceTracingEnabled) {
                Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
            }
        }
    }

    private static native ImageDecoder nCreate(long asset,
            boolean preferAnimation, Source src) throws IOException;
    private static native ImageDecoder nCreate(ByteBuffer buffer, int position, int limit,