Import Android SDK Platform P [4697573]

/google/data/ro/projects/android/fetch_artifact \
    --bid 4697573 \
    --target sdk_phone_armv7-win_sdk \
    sdk-repo-linux-sources-4697573.zip

AndroidVersion.ApiLevel has been modified to appear as 28

Change-Id: If80578c3c657366cc9cf75f8db13d46e2dd4e077
diff --git a/android/graphics/BaseCanvas.java b/android/graphics/BaseCanvas.java
index 627d551..71ee6c2 100644
--- a/android/graphics/BaseCanvas.java
+++ b/android/graphics/BaseCanvas.java
@@ -22,8 +22,7 @@
 import android.annotation.Size;
 import android.graphics.Canvas.VertexMode;
 import android.text.GraphicsOperations;
-import android.text.MeasuredParagraph;
-import android.text.MeasuredText;
+import android.text.PrecomputedText;
 import android.text.SpannableString;
 import android.text.SpannedString;
 import android.text.TextUtils;
@@ -42,13 +41,19 @@
     /**
      * Should only be assigned in constructors (or setBitmap if software canvas),
      * freed by NativeAllocation.
+     * @hide
      */
     protected long mNativeCanvasWrapper;
 
     /**
      * Used to determine when compatibility scaling is in effect.
+     * @hide
      */
     protected int mScreenDensity = Bitmap.DENSITY_NONE;
+
+    /**
+     * @hide
+     */
     protected int mDensity = Bitmap.DENSITY_NONE;
     private boolean mAllowHwBitmapsInSwMode = false;
 
@@ -455,8 +460,7 @@
 
         throwIfHasHwBitmapInSwMode(paint);
         nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
-                x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */,
-                0 /* measured text offset */);
+                x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */);
     }
 
     public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart,
@@ -487,19 +491,16 @@
             char[] buf = TemporaryBuffer.obtain(contextLen);
             TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
             long measuredTextPtr = 0;
-            int measuredTextOffset = 0;
-            if (text instanceof MeasuredText) {
-                MeasuredText mt = (MeasuredText) text;
+            if (text instanceof PrecomputedText) {
+                PrecomputedText mt = (PrecomputedText) text;
                 int paraIndex = mt.findParaIndex(start);
                 if (end <= mt.getParagraphEnd(paraIndex)) {
-                    // Only suppor the same paragraph.
+                    // Only suppor the text in the same paragraph.
                     measuredTextPtr = mt.getMeasuredParagraph(paraIndex).getNativePtr();
-                    measuredTextOffset = start - mt.getParagraphStart(paraIndex);
                 }
             }
             nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
-                    0, contextLen, x, y, isRtl, paint.getNativeInstance(),
-                    measuredTextPtr, measuredTextOffset);
+                    0, contextLen, x, y, isRtl, paint.getNativeInstance(), measuredTextPtr);
             TemporaryBuffer.recycle(buf);
         }
     }
@@ -541,10 +542,19 @@
         return mAllowHwBitmapsInSwMode;
     }
 
+    /**
+     * @hide
+     */
+    protected void onHwBitmapInSwMode() {
+        if (!mAllowHwBitmapsInSwMode) {
+            throw new IllegalArgumentException(
+                    "Software rendering doesn't support hardware bitmaps");
+        }
+    }
+
     private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
-        if (!mAllowHwBitmapsInSwMode && !isHardwareAccelerated()
-                && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
-            throw new IllegalStateException("Software rendering doesn't support hardware bitmaps");
+        if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
+            onHwBitmapInSwMode();
         }
     }
 
@@ -639,7 +649,7 @@
 
     private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
             int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
-            long nativeMeasuredText, int measuredTextOffset);
+            long nativePrecomputedText);
 
     private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
             long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint);
diff --git a/android/graphics/BaseCanvas_Delegate.java b/android/graphics/BaseCanvas_Delegate.java
index 89fccc7..9260099 100644
--- a/android/graphics/BaseCanvas_Delegate.java
+++ b/android/graphics/BaseCanvas_Delegate.java
@@ -507,7 +507,8 @@
     @LayoutlibDelegate
     /*package*/ static void nDrawTextRun(long nativeCanvas, char[] text,
             int start, int count, int contextStart, int contextCount,
-            float x, float y, boolean isRtl, long paint) {
+            float x, float y, boolean isRtl, long paint,
+            long nativeMeasuredText, int measuredTextOffset) {
         drawText(nativeCanvas, text, start, count, x, y, isRtl ? Paint.BIDI_RTL : Paint.BIDI_LTR, paint);
     }
 
diff --git a/android/graphics/BidiRenderer.java b/android/graphics/BidiRenderer.java
index 3dc1d41..9a102c1 100644
--- a/android/graphics/BidiRenderer.java
+++ b/android/graphics/BidiRenderer.java
@@ -43,7 +43,10 @@
  */
 @SuppressWarnings("deprecation")
 public class BidiRenderer {
-    private static String JAVA_VENDOR = System.getProperty("java.vendor");
+    private static final String JETBRAINS_VENDOR_ID = "JetBrains s.r.o";
+    private static final String JAVA_VENDOR = System.getProperty("java.vendor");
+    /** When scaleX is bigger than this, we need to apply the workaround for http://b.android.com/211659 */
+    private static final double SCALEX_WORKAROUND_LIMIT = 9;
 
     private static class ScriptRun {
         private final int start;
@@ -209,23 +212,42 @@
      */
     private void render(int start, int limit, Font font, int flag, float[] advances,
             int advancesIndex, boolean draw) {
+        FontRenderContext frc = mGraphics != null ? mGraphics.getFontRenderContext() :
+                    Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
 
-        FontRenderContext frc;
-        if (mGraphics != null) {
-            frc = mGraphics.getFontRenderContext();
-        } else {
-            frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
+        boolean frcIsAntialiased = frc.isAntiAliased();
+        boolean useAntialiasing = mPaint.isAntiAliased();
 
-            // Metrics obtained this way don't have anti-aliasing set. So,
-            // we create a new FontRenderContext with anti-aliasing set.
+        if (frcIsAntialiased) {
+            if (!useAntialiasing) {
+                // The context has antialiasing enabled but the paint does not. We need to
+                // disable it
+                frc = new FontRenderContext(font.getTransform(), false,
+                        frc.usesFractionalMetrics());
+            } else {
+                // In this case both the paint and the context antialising match but we need
+                // to check for a bug in the JDK
+                // Workaround for http://b.android.com/211659 (disable antialiasing)
+                if (font.isTransformed()) {
+                    AffineTransform transform = font.getTransform();
+                    if (transform.getScaleX() >= SCALEX_WORKAROUND_LIMIT &&
+                            JETBRAINS_VENDOR_ID.equals(JAVA_VENDOR)) {
+                        frc = new FontRenderContext(transform, false, frc.usesFractionalMetrics());
+                    }
+                }
+            }
+        } else if (useAntialiasing) {
+            // The context does not have antialiasing enabled but the paint does. We need to
+            // enable it unless we need to avoid the JDK bug
+
             AffineTransform transform = font.getTransform();
-            if (mPaint.isAntiAliased() &&
-                    // Workaround for http://b.android.com/211659
-                    (transform.getScaleX() <= 9.9 ||
-                    !"JetBrains s.r.o".equals(JAVA_VENDOR))) {
-                frc = new FontRenderContext(transform, true, frc.usesFractionalMetrics());
+            // Workaround for http://b.android.com/211659 (disable antialiasing)
+            if (transform.getScaleX() < SCALEX_WORKAROUND_LIMIT ||
+                    !JETBRAINS_VENDOR_ID.equals(JAVA_VENDOR)) {
+                frc = new FontRenderContext(font.getTransform(), true, frc.usesFractionalMetrics());
             }
         }
+
         GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
         int ng = gv.getNumGlyphs();
         int[] ci = gv.getGlyphCharIndices(0, ng, null);
diff --git a/android/graphics/Bitmap.java b/android/graphics/Bitmap.java
index 0072012..e8ede94 100644
--- a/android/graphics/Bitmap.java
+++ b/android/graphics/Bitmap.java
@@ -29,6 +29,10 @@
 import android.os.Trace;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
+
 import libcore.util.NativeAllocationRegistry;
 
 import java.io.OutputStream;
@@ -457,6 +461,11 @@
          *
          * This configuration may be useful when using opaque bitmaps
          * that do not require high color fidelity.
+         *
+         * <p>Use this formula to pack into 16 bits:</p>
+         * <pre class="prettyprint">
+         * short color = (R & 0x1f) << 11 | (G & 0x3f) << 5 | (B & 0x1f);
+         * </pre>
          */
         RGB_565     (3),
 
@@ -489,6 +498,11 @@
          *
          * This configuration is very flexible and offers the best
          * quality. It should be used whenever possible.
+         *
+         * <p>Use this formula to pack into 32 bits:</p>
+         * <pre class="prettyprint">
+         * int color = (A & 0xff) << 24 | (B & 0xff) << 16 | (G & 0xff) << 8 | (R & 0xff);
+         * </pre>
          */
         ARGB_8888   (5),
 
@@ -499,6 +513,11 @@
          *
          * This configuration is particularly suited for wide-gamut and
          * HDR content.
+         *
+         * <p>Use this formula to pack into 64 bits:</p>
+         * <pre class="prettyprint">
+         * long color = (A & 0xffff) << 48 | (B & 0xffff) << 32 | (G & 0xffff) << 16 | (R & 0xffff);
+         * </pre>
          */
         RGBA_F16    (6),
 
@@ -1171,6 +1190,82 @@
     }
 
     /**
+     * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+     *
+     * Equivalent to calling {@link #createBitmap(Picture, int, int, Config)} with
+     * width and height the same as the Picture's width and height and a Config.HARDWARE
+     * config.
+     *
+     * @param source The recorded {@link Picture} of drawing commands that will be
+     *               drawn into the returned Bitmap.
+     * @return An immutable bitmap with a HARDWARE config whose contents are created
+     * from the recorded drawing commands in the Picture source.
+     */
+    public static @NonNull Bitmap createBitmap(@NonNull Picture source) {
+        return createBitmap(source, source.getWidth(), source.getHeight(), Config.HARDWARE);
+    }
+
+    /**
+     * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
+     *
+     * The bitmap will be immutable with the given width and height. If the width and height
+     * are not the same as the Picture's width & height, the Picture will be scaled to
+     * fit the given width and height.
+     *
+     * @param source The recorded {@link Picture} of drawing commands that will be
+     *               drawn into the returned Bitmap.
+     * @param width The width of the bitmap to create. The picture's width will be
+     *              scaled to match if necessary.
+     * @param height The height of the bitmap to create. The picture's height will be
+     *              scaled to match if necessary.
+     * @param config The {@link Config} of the created bitmap. If this is null then
+     *               the bitmap will be {@link Config#HARDWARE}.
+     *
+     * @return An immutable bitmap with a HARDWARE config whose contents are created
+     * from the recorded drawing commands in the Picture source.
+     */
+    public static @NonNull Bitmap createBitmap(@NonNull Picture source, int width, int height,
+            @NonNull Config config) {
+        if (width <= 0 || height <= 0) {
+            throw new IllegalArgumentException("width & height must be > 0");
+        }
+        if (config == null) {
+            throw new IllegalArgumentException("Config must not be null");
+        }
+        if (source.requiresHardwareAcceleration() && config != Config.HARDWARE) {
+            StrictMode.noteSlowCall("GPU readback");
+        }
+        if (config == Config.HARDWARE || source.requiresHardwareAcceleration()) {
+            final RenderNode node = RenderNode.create("BitmapTemporary", null);
+            node.setLeftTopRightBottom(0, 0, width, height);
+            node.setClipToBounds(false);
+            final DisplayListCanvas canvas = node.start(width, height);
+            if (source.getWidth() != width || source.getHeight() != height) {
+                canvas.scale(width / (float) source.getWidth(),
+                        height / (float) source.getHeight());
+            }
+            canvas.drawPicture(source);
+            node.end(canvas);
+            Bitmap bitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
+            if (config != Config.HARDWARE) {
+                bitmap = bitmap.copy(config, false);
+            }
+            return bitmap;
+        } else {
+            Bitmap bitmap = Bitmap.createBitmap(width, height, config);
+            Canvas canvas = new Canvas(bitmap);
+            if (source.getWidth() != width || source.getHeight() != height) {
+                canvas.scale(width / (float) source.getWidth(),
+                        height / (float) source.getHeight());
+            }
+            canvas.drawPicture(source);
+            canvas.setBitmap(null);
+            bitmap.makeImmutable();
+            return bitmap;
+        }
+    }
+
+    /**
      * Returns an optional array of private data, used by the UI system for
      * some bitmaps. Not intended to be called by applications.
      */
@@ -1259,6 +1354,12 @@
         return mIsMutable;
     }
 
+    /** @hide */
+    public final void makeImmutable() {
+        // todo mIsMutable = false;
+        // todo nMakeImmutable();
+    }
+
     /**
      * <p>Indicates whether pixels stored in this bitmaps are stored pre-multiplied.
      * When a pixel is pre-multiplied, the RGB components have been multiplied by
@@ -1568,6 +1669,8 @@
         if (mColorSpace == null) {
             if (nativeIsSRGB(mNativePtr)) {
                 mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
+            } else if (getConfig() == Config.HARDWARE && nativeIsSRGBLinear(mNativePtr)) {
+                mColorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
             } else {
                 float[] xyz = new float[9];
                 float[] params = new float[7];
@@ -1991,5 +2094,6 @@
     private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap);
     private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params);
     private static native boolean nativeIsSRGB(long nativePtr);
+    private static native boolean nativeIsSRGBLinear(long nativePtr);
     private static native void nativeCopyColorSpace(long srcBitmap, long dstBitmap);
 }
diff --git a/android/graphics/BitmapFactory.java b/android/graphics/BitmapFactory.java
index f5bf754..7ea35e7 100644
--- a/android/graphics/BitmapFactory.java
+++ b/android/graphics/BitmapFactory.java
@@ -18,6 +18,8 @@
 
 import static android.graphics.BitmapFactory.Options.validate;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.os.Trace;
@@ -518,8 +520,9 @@
      *         is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
      *         function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
      */
-    public static Bitmap decodeResourceStream(Resources res, TypedValue value,
-            InputStream is, Rect pad, Options opts) {
+    @Nullable
+    public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,
+            @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {
         validate(opts);
         if (opts == null) {
             opts = new Options();
@@ -707,7 +710,9 @@
      * <code>is.mark(1024)</code> would be called. As of
      * {@link android.os.Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
      */
-    public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
+    @Nullable
+    public static Bitmap decodeStream(@Nullable InputStream is, @Nullable Rect outPadding,
+            @Nullable Options opts) {
         // we don't throw in this case, thus allowing the caller to only check
         // the cache, and not force the image to be decoded.
         if (is == null) {
@@ -742,7 +747,8 @@
      * Private helper function for decoding an InputStream natively. Buffers the input enough to
      * do a rewind as needed, and supplies temporary storage if necessary. is MUST NOT be null.
      */
-    private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts) {
+    private static Bitmap decodeStreamInternal(@NonNull InputStream is,
+            @Nullable Rect outPadding, @Nullable Options opts) {
         // ASSERT(is != null);
         byte [] tempStorage = null;
         if (opts != null) tempStorage = opts.inTempStorage;
diff --git a/android/graphics/Canvas.java b/android/graphics/Canvas.java
index f5e8633..3cc92bc 100644
--- a/android/graphics/Canvas.java
+++ b/android/graphics/Canvas.java
@@ -47,6 +47,7 @@
  * Canvas and Drawables</a> developer guide.</p></div>
  */
 public class Canvas extends BaseCanvas {
+    private static int sCompatiblityVersion = 0;
     /** @hide */
     public static boolean sCompatibilityRestore = false;
     /** @hide */
@@ -306,7 +307,7 @@
 
     /**
      * Restore the current matrix when restore() is called.
-     *
+     * @removed
      * @deprecated Use the flagless version of {@link #save()}, {@link #saveLayer(RectF, Paint)} or
      *             {@link #saveLayerAlpha(RectF, int)}. For saveLayer() calls the matrix
      *             was always restored for {@link #isHardwareAccelerated() Hardware accelerated}
@@ -318,6 +319,7 @@
     /**
      * Restore the current clip when restore() is called.
      *
+     * @removed
      * @deprecated Use the flagless version of {@link #save()}, {@link #saveLayer(RectF, Paint)} or
      *             {@link #saveLayerAlpha(RectF, int)}. For saveLayer() calls the clip
      *             was always restored for {@link #isHardwareAccelerated() Hardware accelerated}
@@ -329,6 +331,7 @@
     /**
      * The layer requires a per-pixel alpha channel.
      *
+     * @removed
      * @deprecated This flag is ignored. Use the flagless version of {@link #saveLayer(RectF, Paint)}
      *             {@link #saveLayerAlpha(RectF, int)}.
      */
@@ -337,6 +340,7 @@
     /**
      * The layer requires full 8-bit precision for each color channel.
      *
+     * @removed
      * @deprecated This flag is ignored. Use the flagless version of {@link #saveLayer(RectF, Paint)}
      *             {@link #saveLayerAlpha(RectF, int)}.
      */
@@ -349,6 +353,7 @@
      * <code>saveLayerAlpha()</code> variants. Not passing this flag generally
      * triggers extremely poor performance with hardware accelerated rendering.
      *
+     * @removed
      * @deprecated This flag results in poor performance and the same effect can be achieved with
      *             a single layer or multiple draw commands with different clips.
      *
@@ -367,6 +372,14 @@
      */
     public static final int ALL_SAVE_FLAG = 0x1F;
 
+    private static void checkValidSaveFlags(int saveFlags) {
+        if (sCompatiblityVersion >= Build.VERSION_CODES.P
+                && saveFlags != ALL_SAVE_FLAG) {
+            throw new IllegalArgumentException(
+                    "Invalid Layer Save Flag - only ALL_SAVE_FLAGS is allowed");
+        }
+    }
+
     /**
      * Saves the current matrix and clip onto a private stack.
      * <p>
@@ -393,6 +406,7 @@
      * restore() is made, those calls will be forgotten, and the settings that
      * existed before the save() will be reinstated.
      *
+     * @removed
      * @deprecated Use {@link #save()} instead.
      * @param saveFlags flag bits that specify which parts of the Canvas state
      *                  to save/restore
@@ -407,10 +421,8 @@
      * redirects drawing to an offscreen bitmap.
      * <p class="note"><strong>Note:</strong> this method is very expensive,
      * incurring more than double rendering cost for contained content. Avoid
-     * using this method, especially if the bounds provided are large, or if
-     * the {@link #CLIP_TO_LAYER_SAVE_FLAG} is omitted from the
-     * {@code saveFlags} parameter. It is recommended to use a
-     * {@link android.view.View#LAYER_TYPE_HARDWARE hardware layer} on a View
+     * using this method, especially if the bounds provided are large. It is
+     * recommended to use a {@link android.view.View#LAYER_TYPE_HARDWARE hardware layer} on a View
      * to apply an xfermode, color filter, or alpha, as it will perform much
      * better than this method.
      * <p>
@@ -424,6 +436,9 @@
      * {@link Paint#getColorFilter() ColorFilter} are applied when the
      * offscreen bitmap is drawn back when restore() is called.
      *
+     * As of API Level API level {@value Build.VERSION_CODES#P} the only valid
+     * {@code saveFlags} is {@link #ALL_SAVE_FLAG}.  All other flags are ignored.
+     *
      * @deprecated Use {@link #saveLayer(RectF, Paint)} instead.
      * @param bounds May be null. The maximum size the offscreen bitmap
      *               needs to be (in local coordinates)
@@ -437,7 +452,9 @@
         if (bounds == null) {
             bounds = new RectF(getClipBounds());
         }
-        return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint, saveFlags);
+        checkValidSaveFlags(saveFlags);
+        return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint,
+                ALL_SAVE_FLAG);
     }
 
     /**
@@ -471,15 +488,26 @@
     }
 
     /**
+     * @hide
+     */
+    public int saveUnclippedLayer(int left, int top, int right, int bottom) {
+        return nSaveLayer(mNativeCanvasWrapper, left, top, right, bottom, 0, 0);
+    }
+
+    /**
      * Helper version of saveLayer() that takes 4 values rather than a RectF.
      *
+     * As of API Level API level {@value Build.VERSION_CODES#P} the only valid
+     * {@code saveFlags} is {@link #ALL_SAVE_FLAG}.  All other flags are ignored.
+     *
      * @deprecated Use {@link #saveLayer(float, float, float, float, Paint)} instead.
      */
     public int saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint,
             @Saveflags int saveFlags) {
+        checkValidSaveFlags(saveFlags);
         return nSaveLayer(mNativeCanvasWrapper, left, top, right, bottom,
                 paint != null ? paint.getNativeInstance() : 0,
-                saveFlags);
+                ALL_SAVE_FLAG);
     }
 
     /**
@@ -495,10 +523,8 @@
      * redirects drawing to an offscreen bitmap.
      * <p class="note"><strong>Note:</strong> this method is very expensive,
      * incurring more than double rendering cost for contained content. Avoid
-     * using this method, especially if the bounds provided are large, or if
-     * the {@link #CLIP_TO_LAYER_SAVE_FLAG} is omitted from the
-     * {@code saveFlags} parameter. It is recommended to use a
-     * {@link android.view.View#LAYER_TYPE_HARDWARE hardware layer} on a View
+     * using this method, especially if the bounds provided are large. It is
+     * recommended to use a {@link android.view.View#LAYER_TYPE_HARDWARE hardware layer} on a View
      * to apply an xfermode, color filter, or alpha, as it will perform much
      * better than this method.
      * <p>
@@ -510,6 +536,9 @@
      * The {@code alpha} parameter is applied when the offscreen bitmap is
      * drawn back when restore() is called.
      *
+     * As of API Level API level {@value Build.VERSION_CODES#P} the only valid
+     * {@code saveFlags} is {@link #ALL_SAVE_FLAG}.  All other flags are ignored.
+     *
      * @deprecated Use {@link #saveLayerAlpha(RectF, int)} instead.
      * @param bounds    The maximum size the offscreen bitmap needs to be
      *                  (in local coordinates)
@@ -523,7 +552,9 @@
         if (bounds == null) {
             bounds = new RectF(getClipBounds());
         }
-        return saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom, alpha, saveFlags);
+        checkValidSaveFlags(saveFlags);
+        return saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom, alpha,
+                ALL_SAVE_FLAG);
     }
 
     /**
@@ -542,13 +573,17 @@
     /**
      * Helper for saveLayerAlpha() that takes 4 values instead of a RectF.
      *
+     * As of API Level API level {@value Build.VERSION_CODES#P} the only valid
+     * {@code saveFlags} is {@link #ALL_SAVE_FLAG}.  All other flags are ignored.
+     *
      * @deprecated Use {@link #saveLayerAlpha(float, float, float, float, int)} instead.
      */
     public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
             @Saveflags int saveFlags) {
+        checkValidSaveFlags(saveFlags);
         alpha = Math.min(255, Math.max(0, alpha));
         return nSaveLayerAlpha(mNativeCanvasWrapper, left, top, right, bottom,
-                                     alpha, saveFlags);
+                                     alpha, ALL_SAVE_FLAG);
     }
 
     /**
@@ -738,6 +773,14 @@
         return m;
     }
 
+    private static void checkValidClipOp(@NonNull Region.Op op) {
+        if (sCompatiblityVersion >= Build.VERSION_CODES.P
+                && op != Region.Op.INTERSECT && op != Region.Op.DIFFERENCE) {
+            throw new IllegalArgumentException(
+                    "Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed");
+        }
+    }
+
     /**
      * Modify the current clip with the specified rectangle.
      *
@@ -750,9 +793,13 @@
      * are intended to only expand the clip as a result of a restore operation. This enables a view
      * parent to clip a canvas to clearly define the maximal drawing area of its children. The
      * recommended alternative calls are {@link #clipRect(RectF)} and {@link #clipOutRect(RectF)};
+     *
+     * As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and
+     * {@link Region.Op#DIFFERENCE} are valid Region.Op parameters.
      */
     @Deprecated
     public boolean clipRect(@NonNull RectF rect, @NonNull Region.Op op) {
+        checkValidClipOp(op);
         return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
                 op.nativeInt);
     }
@@ -770,9 +817,13 @@
      * are intended to only expand the clip as a result of a restore operation. This enables a view
      * parent to clip a canvas to clearly define the maximal drawing area of its children. The
      * recommended alternative calls are {@link #clipRect(Rect)} and {@link #clipOutRect(Rect)};
+     *
+     * As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and
+     * {@link Region.Op#DIFFERENCE} are valid Region.Op parameters.
      */
     @Deprecated
     public boolean clipRect(@NonNull Rect rect, @NonNull Region.Op op) {
+        checkValidClipOp(op);
         return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
                 op.nativeInt);
     }
@@ -846,10 +897,14 @@
      * parent to clip a canvas to clearly define the maximal drawing area of its children. The
      * recommended alternative calls are {@link #clipRect(float,float,float,float)} and
      * {@link #clipOutRect(float,float,float,float)};
+     *
+     * As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and
+     * {@link Region.Op#DIFFERENCE} are valid Region.Op parameters.
      */
     @Deprecated
     public boolean clipRect(float left, float top, float right, float bottom,
             @NonNull Region.Op op) {
+        checkValidClipOp(op);
         return nClipRect(mNativeCanvasWrapper, left, top, right, bottom, op.nativeInt);
     }
 
@@ -932,9 +987,13 @@
      * parent to clip a canvas to clearly define the maximal drawing area of its children. The
      * recommended alternative calls are {@link #clipPath(Path)} and
      * {@link #clipOutPath(Path)};
+     *
+     * As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and
+     * {@link Region.Op#DIFFERENCE} are valid Region.Op parameters.
      */
     @Deprecated
     public boolean clipPath(@NonNull Path path, @NonNull Region.Op op) {
+        checkValidClipOp(op);
         return nClipPath(mNativeCanvasWrapper, path.readOnlyNI(), op.nativeInt);
     }
 
@@ -1219,10 +1278,17 @@
         nFreeTextLayoutCaches();
     }
 
+    /** @hide */
+    public static void setCompatibilityVersion(int apiLevel) {
+        sCompatiblityVersion = apiLevel;
+        nSetCompatibilityVersion(apiLevel);
+    }
+
     private static native void nFreeCaches();
     private static native void nFreeTextLayoutCaches();
     private static native long nInitRaster(Bitmap bitmap);
     private static native long nGetNativeFinalizer();
+    private static native void nSetCompatibilityVersion(int apiLevel);
 
     // ---------------- @FastNative -------------------
 
@@ -1486,6 +1552,10 @@
      * across the top of the bitmap from left to right. A more general version of this method is
      * drawVertices().
      *
+     * Prior to API level {@value Build.VERSION_CODES#P} vertOffset and colorOffset were ignored,
+     * effectively treating them as zeros. In API level {@value Build.VERSION_CODES#P} and above
+     * these parameters will be respected.
+     *
      * @param bitmap The bitmap to draw using the mesh
      * @param meshWidth The number of columns in the mesh. Nothing is drawn if this is 0
      * @param meshHeight The number of rows in the mesh. Nothing is drawn if this is 0
diff --git a/android/graphics/Canvas_Delegate.java b/android/graphics/Canvas_Delegate.java
index 9d8af38..a23244b 100644
--- a/android/graphics/Canvas_Delegate.java
+++ b/android/graphics/Canvas_Delegate.java
@@ -470,6 +470,11 @@
         return sFinalizer;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static void nSetCompatibilityVersion(int apiLevel) {
+        // Unsupported by layoutlib, do nothing
+    }
+
     private Canvas_Delegate(Bitmap_Delegate bitmap) {
         super(bitmap);
     }
diff --git a/android/graphics/EmbossMaskFilter.java b/android/graphics/EmbossMaskFilter.java
index a9e180f..003678a 100644
--- a/android/graphics/EmbossMaskFilter.java
+++ b/android/graphics/EmbossMaskFilter.java
@@ -20,12 +20,15 @@
     /**
      * Create an emboss maskfilter
      *
+     * @deprecated This subclass is not supported and should not be instantiated.
+     *
      * @param direction  array of 3 scalars [x, y, z] specifying the direction of the light source
      * @param ambient    0...1 amount of ambient light
      * @param specular   coefficient for specular highlights (e.g. 8)
      * @param blurRadius amount to blur before applying lighting (e.g. 3)
      * @return           the emboss maskfilter
      */
+    @Deprecated
     public EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) {
         if (direction.length < 3) {
             throw new ArrayIndexOutOfBoundsException();
diff --git a/android/graphics/FontFamily.java b/android/graphics/FontFamily.java
index d77e601..c69eb32 100644
--- a/android/graphics/FontFamily.java
+++ b/android/graphics/FontFamily.java
@@ -19,11 +19,13 @@
 import android.annotation.Nullable;
 import android.content.res.AssetManager;
 import android.graphics.fonts.FontVariationAxis;
-import android.text.FontConfig;
 import android.text.TextUtils;
 import android.util.Log;
+
 import dalvik.annotation.optimization.CriticalNative;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -38,6 +40,14 @@
 
     private static String TAG = "FontFamily";
 
+    private static final NativeAllocationRegistry sBuilderRegistry = new NativeAllocationRegistry(
+            FontFamily.class.getClassLoader(), nGetBuilderReleaseFunc(), 64);
+
+    private @Nullable Runnable mNativeBuilderCleaner;
+
+    private static final NativeAllocationRegistry sFamilyRegistry = new NativeAllocationRegistry(
+            FontFamily.class.getClassLoader(), nGetFamilyReleaseFunc(), 64);
+
     /**
      * @hide
      */
@@ -48,6 +58,7 @@
 
     public FontFamily() {
         mBuilderPtr = nInitBuilder(null, 0);
+        mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr);
     }
 
     public FontFamily(@Nullable String[] langs, int variant) {
@@ -60,6 +71,7 @@
             langsString = TextUtils.join(",", langs);
         }
         mBuilderPtr = nInitBuilder(langsString, variant);
+        mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr);
     }
 
     /**
@@ -73,7 +85,11 @@
             throw new IllegalStateException("This FontFamily is already frozen");
         }
         mNativePtr = nCreateFamily(mBuilderPtr);
+        mNativeBuilderCleaner.run();
         mBuilderPtr = 0;
+        if (mNativePtr != 0) {
+            sFamilyRegistry.registerNativeAllocation(this, mNativePtr);
+        }
         return mNativePtr != 0;
     }
 
@@ -81,24 +97,10 @@
         if (mBuilderPtr == 0) {
             throw new IllegalStateException("This FontFamily is already frozen or abandoned");
         }
-        nAbort(mBuilderPtr);
+        mNativeBuilderCleaner.run();
         mBuilderPtr = 0;
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if (mNativePtr != 0) {
-                nUnrefFamily(mNativePtr);
-            }
-            if (mBuilderPtr != 0) {
-                nAbort(mBuilderPtr);
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
     public boolean addFont(String path, int ttcIndex, FontVariationAxis[] axes, int weight,
             int italic) {
         if (mBuilderPtr == 0) {
@@ -160,25 +162,6 @@
                 isItalic);
     }
 
-    /**
-     * Allow creating unsupported FontFamily.
-     *
-     * For compatibility reasons, we still need to create a FontFamily object even if Minikin failed
-     * to find any usable 'cmap' table for some reasons, e.g. broken 'cmap' table, no 'cmap' table
-     * encoded with Unicode code points, etc. Without calling this method, the freeze() method will
-     * return null if Minikin fails to find any usable 'cmap' table. By calling this method, the
-     * freeze() won't fail and will create an empty FontFamily. This empty FontFamily is placed at
-     * the top of the fallback chain but is never used. if we don't create this empty FontFamily
-     * and put it at top, bad things (performance regressions, unexpected glyph selection) will
-     * happen.
-     */
-    public void allowUnsupportedFont() {
-        if (mBuilderPtr == 0) {
-            throw new IllegalStateException("Unable to allow unsupported font.");
-        }
-        nAllowUnsupportedFont(mBuilderPtr);
-    }
-
     // TODO: Remove once internal user stop using private API.
     private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
         return nAddFont(builderPtr, font, ttcIndex, -1, -1);
@@ -190,13 +173,10 @@
     private static native long nCreateFamily(long mBuilderPtr);
 
     @CriticalNative
-    private static native void nAllowUnsupportedFont(long builderPtr);
+    private static native long nGetBuilderReleaseFunc();
 
     @CriticalNative
-    private static native void nAbort(long mBuilderPtr);
-
-    @CriticalNative
-    private static native void nUnrefFamily(long nativePtr);
+    private static native long nGetFamilyReleaseFunc();
     // By passing -1 to weigth argument, the weight value is resolved by OS/2 table in the font.
     // By passing -1 to italic argument, the italic value is resolved by OS/2 table in the font.
     private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex,
diff --git a/android/graphics/FontFamily_Delegate.java b/android/graphics/FontFamily_Delegate.java
index 1e3ebd7..1ad1f8f 100644
--- a/android/graphics/FontFamily_Delegate.java
+++ b/android/graphics/FontFamily_Delegate.java
@@ -27,7 +27,6 @@
 import android.content.res.AssetManager;
 import android.content.res.BridgeAssetManager;
 import android.graphics.fonts.FontVariationAxis;
-import android.text.FontConfig;
 
 import java.awt.Font;
 import java.awt.FontFormatException;
@@ -38,7 +37,6 @@
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -453,14 +451,6 @@
         sManager.removeJavaReferenceFor(builderPtr);
     }
 
-    /**
-     * @see FontFamily#allowUnsupportedFont
-     */
-    @LayoutlibDelegate
-    /*package*/ static void nAllowUnsupportedFont(long builderPtr) {
-        // Do nothing here as this is used for Minikin fonts
-    }
-
     // ---- private helper methods ----
 
     private void init() {
diff --git a/android/graphics/FontListParser.java b/android/graphics/FontListParser.java
index 9f672e3..431d0e0 100644
--- a/android/graphics/FontListParser.java
+++ b/android/graphics/FontListParser.java
@@ -16,16 +16,13 @@
 
 package android.graphics;
 
-import android.text.FontConfig;
 import android.graphics.fonts.FontVariationAxis;
+import android.text.FontConfig;
 import android.util.Xml;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import android.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
diff --git a/android/graphics/ImageDecoder.java b/android/graphics/ImageDecoder.java
index 3de050b..506eab5 100644
--- a/android/graphics/ImageDecoder.java
+++ b/android/graphics/ImageDecoder.java
@@ -16,50 +16,34 @@
 
 package android.graphics;
 
-import static android.system.OsConstants.SEEK_CUR;
-import static android.system.OsConstants.SEEK_SET;
-
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.RawRes;
 import android.content.ContentResolver;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager;
+import android.content.res.AssetManager.AssetInputStream;
 import android.content.res.Resources;
 import android.graphics.drawable.AnimatedImageDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.NinePatchDrawable;
 import android.net.Uri;
-import android.util.Size;
-import android.system.ErrnoException;
-import android.system.Os;
 import android.util.DisplayMetrics;
+import android.util.Size;
 import android.util.TypedValue;
 
-import libcore.io.IoUtils;
-import dalvik.system.CloseGuard;
-
 import java.nio.ByteBuffer;
 import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ArrayIndexOutOfBoundsException;
 import java.lang.AutoCloseable;
 import java.lang.NullPointerException;
-import java.lang.RuntimeException;
 import java.lang.annotation.Retention;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  *  Class for decoding images as {@link Bitmap}s or {@link Drawable}s.
  */
 public final class ImageDecoder implements AutoCloseable {
+
     /**
      *  Source of the encoded image data.
      */
@@ -100,7 +84,7 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            return nCreate(mData, mOffset, mLength);
+            return new ImageDecoder();
         }
     }
 
@@ -112,12 +96,7 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            if (!mBuffer.isDirect() && mBuffer.hasArray()) {
-                int offset = mBuffer.arrayOffset() + mBuffer.position();
-                int length = mBuffer.limit() - mBuffer.position();
-                return nCreate(mBuffer.array(), offset, length);
-            }
-            return nCreate(mBuffer, mBuffer.position(), mBuffer.limit());
+            return new ImageDecoder();
         }
     }
 
@@ -132,89 +111,13 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            AssetFileDescriptor assetFd = null;
-            try {
-                if (mUri.getScheme() == ContentResolver.SCHEME_CONTENT) {
-                    assetFd = mResolver.openTypedAssetFileDescriptor(mUri,
-                            "image/*", null);
-                } else {
-                    assetFd = mResolver.openAssetFileDescriptor(mUri, "r");
-                }
-            } catch (FileNotFoundException e) {
-                // Some images cannot be opened as AssetFileDescriptors (e.g.
-                // bmp, ico). Open them as InputStreams.
-                InputStream is = mResolver.openInputStream(mUri);
-                if (is == null) {
-                    throw new FileNotFoundException(mUri.toString());
-                }
-
-                return createFromStream(is);
-            }
-
-            final FileDescriptor fd = assetFd.getFileDescriptor();
-            final long offset = assetFd.getStartOffset();
-
-            ImageDecoder decoder = null;
-            try {
-                try {
-                    Os.lseek(fd, offset, SEEK_SET);
-                    decoder = nCreate(fd);
-                } catch (ErrnoException e) {
-                    decoder = createFromStream(new FileInputStream(fd));
-                }
-            } finally {
-                if (decoder == null) {
-                    IoUtils.closeQuietly(assetFd);
-                } else {
-                    decoder.mAssetFd = assetFd;
-                }
-            }
-            return decoder;
+            return new ImageDecoder();
         }
     }
 
-    @NonNull
-    private static ImageDecoder createFromFile(@NonNull File file) throws IOException {
-        FileInputStream stream = new FileInputStream(file);
-        FileDescriptor fd = stream.getFD();
-        try {
-            Os.lseek(fd, 0, SEEK_CUR);
-        } catch (ErrnoException e) {
-            return createFromStream(stream);
-        }
-
-        ImageDecoder decoder = null;
-        try {
-            decoder = nCreate(fd);
-        } finally {
-            if (decoder == null) {
-                IoUtils.closeQuietly(stream);
-            } else {
-                decoder.mInputStream = stream;
-            }
-        }
-        return decoder;
-    }
-
-    @NonNull
-    private static ImageDecoder createFromStream(@NonNull InputStream is) throws IOException {
-        // Arbitrary size matches BitmapFactory.
-        byte[] storage = new byte[16 * 1024];
-        ImageDecoder decoder = null;
-        try {
-            decoder = nCreate(is, storage);
-        } finally {
-            if (decoder == null) {
-                IoUtils.closeQuietly(is);
-            } else {
-                decoder.mInputStream = is;
-                decoder.mTempStorage = storage;
-            }
-        }
-
-        return decoder;
-    }
-
+    /**
+     * For backwards compatibility, this does *not* close the InputStream.
+     */
     private static class InputStreamSource extends Source {
         InputStreamSource(Resources res, InputStream is, int inputDensity) {
             if (is == null) {
@@ -237,16 +140,46 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
+            return new ImageDecoder();
+        }
+    }
 
-            synchronized (this) {
-                if (mInputStream == null) {
-                    throw new IOException("Cannot reuse InputStreamSource");
-                }
-                InputStream is = mInputStream;
-                mInputStream = null;
-                return createFromStream(is);
+    /**
+     * Takes ownership of the AssetInputStream.
+     *
+     * @hide
+     */
+    public static class AssetInputStreamSource extends Source {
+        public AssetInputStreamSource(@NonNull AssetInputStream ais,
+                @NonNull Resources res, @NonNull TypedValue value) {
+            mAssetInputStream = ais;
+            mResources = res;
+
+            if (value.density == TypedValue.DENSITY_DEFAULT) {
+                mDensity = DisplayMetrics.DENSITY_DEFAULT;
+            } else if (value.density != TypedValue.DENSITY_NONE) {
+                mDensity = value.density;
+            } else {
+                mDensity = Bitmap.DENSITY_NONE;
             }
         }
+
+        private AssetInputStream mAssetInputStream;
+        private final Resources  mResources;
+        private final int        mDensity;
+
+        @Override
+        public Resources getResources() { return mResources; }
+
+        @Override
+        public int getDensity() {
+            return mDensity;
+        }
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            return new ImageDecoder();
+        }
     }
 
     private static class ResourceSource extends Source {
@@ -268,34 +201,7 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            // This is just used in order to access the underlying Asset and
-            // keep it alive. FIXME: Can we skip creating this object?
-            InputStream is = null;
-            ImageDecoder decoder = null;
-            TypedValue value = new TypedValue();
-            try {
-                is = mResources.openRawResource(mResId, value);
-
-                if (value.density == TypedValue.DENSITY_DEFAULT) {
-                    mResDensity = DisplayMetrics.DENSITY_DEFAULT;
-                } else if (value.density != TypedValue.DENSITY_NONE) {
-                    mResDensity = value.density;
-                }
-
-                if (!(is instanceof AssetManager.AssetInputStream)) {
-                    // This should never happen.
-                    throw new RuntimeException("Resource is not an asset?");
-                }
-                long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
-                decoder = nCreate(asset);
-            } finally {
-                if (decoder == null) {
-                    IoUtils.closeQuietly(is);
-                } else {
-                    decoder.mInputStream = is;
-                }
-            }
-            return decoder;
+            return new ImageDecoder();
         }
     }
 
@@ -308,7 +214,7 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            return createFromFile(mFile);
+            return new ImageDecoder();
         }
     }
 
@@ -316,11 +222,9 @@
      *  Contains information about the encoded image.
      */
     public static class ImageInfo {
-        private final Size mSize;
         private ImageDecoder mDecoder;
 
         private ImageInfo(@NonNull ImageDecoder decoder) {
-            mSize = new Size(decoder.mWidth, decoder.mHeight);
             mDecoder = decoder;
         }
 
@@ -329,7 +233,7 @@
          */
         @NonNull
         public Size getSize() {
-            return mSize;
+            return new Size(0, 0);
         }
 
         /**
@@ -337,7 +241,17 @@
          */
         @NonNull
         public String getMimeType() {
-            return mDecoder.getMimeType();
+            return "";
+        }
+
+        /**
+         * Whether the image is animated.
+         *
+         * <p>Calling {@link #decodeDrawable} will return an
+         * {@link AnimatedImageDrawable}.</p>
+         */
+        public boolean isAnimated() {
+            return mDecoder.mAnimated;
         }
     };
 
@@ -350,7 +264,7 @@
      *  Optional listener supplied to {@link #decodeDrawable} or
      *  {@link #decodeBitmap}.
      */
-    public static interface OnHeaderDecodedListener {
+    public interface OnHeaderDecodedListener {
         /**
          *  Called when the header is decoded and the size is known.
          *
@@ -358,7 +272,7 @@
          *  @param info Information about the encoded image.
          *  @param source that created the decoder.
          */
-        public void onHeaderDecoded(@NonNull ImageDecoder decoder,
+        void onHeaderDecoded(@NonNull ImageDecoder decoder,
                 @NonNull ImageInfo info, @NonNull Source source);
 
     };
@@ -379,15 +293,14 @@
     public static final int ERROR_SOURCE_ERROR      = 3;
 
     @Retention(SOURCE)
-    @IntDef({ ERROR_SOURCE_EXCEPTION, ERROR_SOURCE_INCOMPLETE, ERROR_SOURCE_ERROR })
-    public @interface Error {};
+    public @interface Error {}
 
     /**
      *  Optional listener supplied to the ImageDecoder.
      *
      *  Without this listener, errors will throw {@link java.io.IOException}.
      */
-    public static interface OnPartialImageListener {
+    public interface OnPartialImageListener {
         /**
          *  Called when there is only a partial image to display.
          *
@@ -402,62 +315,14 @@
          *      with partial data. False (which is the default) to abort the
          *      decode and throw {@link java.io.IOException}.
          */
-        public boolean onPartialImage(@Error int error, @NonNull Source source);
-    };
-
-    // Fields
-    private long          mNativePtr;
-    private final int     mWidth;
-    private final int     mHeight;
-    private final boolean mAnimated;
-
-    private int     mDesiredWidth;
-    private int     mDesiredHeight;
-    private int     mAllocator = ALLOCATOR_DEFAULT;
-    private boolean mRequireUnpremultiplied = false;
-    private boolean mMutable = false;
-    private boolean mPreferRamOverQuality = false;
-    private boolean mAsAlphaMask = false;
-    private Rect    mCropRect;
-    private Source  mSource;
-
-    private PostProcessor          mPostProcessor;
-    private OnPartialImageListener mOnPartialImageListener;
-
-    // Objects for interacting with the input.
-    private InputStream         mInputStream;
-    private byte[]              mTempStorage;
-    private AssetFileDescriptor mAssetFd;
-    private final AtomicBoolean mClosed = new AtomicBoolean();
-    private final CloseGuard    mCloseGuard = CloseGuard.get();
-
-    /**
-     * Private constructor called by JNI. {@link #close} must be
-     * called after decoding to delete native resources.
-     */
-    @SuppressWarnings("unused")
-    private ImageDecoder(long nativePtr, int width, int height,
-            boolean animated) {
-        mNativePtr = nativePtr;
-        mWidth = width;
-        mHeight = height;
-        mDesiredWidth = width;
-        mDesiredHeight = height;
-        mAnimated = animated;
-        mCloseGuard.open("close");
+        boolean onPartialImage(@Error int error, @NonNull Source source);
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if (mCloseGuard != null) {
-                mCloseGuard.warnIfOpen();
-            }
+    private boolean mAnimated;
+    private Rect mOutPaddingRect;
 
-            close();
-        } finally {
-            super.finalize();
-        }
+    public ImageDecoder() {
+        mAnimated = true; // This is too avoid throwing an exception in AnimatedImageDrawable
     }
 
     /**
@@ -471,7 +336,7 @@
      *      {@link #decodeDrawable} or {@link #decodeBitmap}.
      */
     @NonNull
-    public static Source createSource(@NonNull Resources res, @RawRes int resId)
+    public static Source createSource(@NonNull Resources res, int resId)
     {
         return new ResourceSource(res, resId);
     }
@@ -505,9 +370,6 @@
     @NonNull
     public static Source createSource(@NonNull byte[] data, int offset,
             int length) throws ArrayIndexOutOfBoundsException {
-        if (data == null) {
-            throw new NullPointerException("null byte[] in createSource!");
-        }
         if (offset < 0 || length < 0 || offset >= data.length ||
                 offset + length > data.length) {
             throw new ArrayIndexOutOfBoundsException(
@@ -580,15 +442,7 @@
      */
     @NonNull
     public Size getSampledSize(int sampleSize) {
-        if (sampleSize <= 0) {
-            throw new IllegalArgumentException("sampleSize must be positive! "
-                    + "provided " + sampleSize);
-        }
-        if (mNativePtr == 0) {
-            throw new IllegalStateException("ImageDecoder is closed!");
-        }
-
-        return nGetSampledSize(mNativePtr, sampleSize);
+        return new Size(0, 0);
     }
 
     // Modifiers
@@ -599,13 +453,6 @@
      *  @param height must be greater than 0.
      */
     public void setResize(int width, int height) {
-        if (width <= 0 || height <= 0) {
-            throw new IllegalArgumentException("Dimensions must be positive! "
-                    + "provided (" + width + ", " + height + ")");
-        }
-
-        mDesiredWidth = width;
-        mDesiredHeight = height;
     }
 
     /**
@@ -617,12 +464,6 @@
      *  @param sampleSize Sampling rate of the encoded image.
      */
     public void setResize(int sampleSize) {
-        Size size = this.getSampledSize(sampleSize);
-        this.setResize(size.getWidth(), size.getHeight());
-    }
-
-    private boolean requestedResize() {
-        return mWidth != mDesiredWidth || mHeight != mDesiredHeight;
     }
 
     // These need to stay in sync with ImageDecoder.cpp's Allocator enum.
@@ -663,8 +504,6 @@
 
     /** @hide **/
     @Retention(SOURCE)
-    @IntDef({ ALLOCATOR_DEFAULT, ALLOCATOR_SOFTWARE, ALLOCATOR_SHARED_MEMORY,
-              ALLOCATOR_HARDWARE })
     public @interface Allocator {};
 
     /**
@@ -674,11 +513,8 @@
      *
      *  @param allocator Type of allocator to use.
      */
-    public void setAllocator(@Allocator int allocator) {
-        if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) {
-            throw new IllegalArgumentException("invalid allocator " + allocator);
-        }
-        mAllocator = allocator;
+    public ImageDecoder setAllocator(@Allocator int allocator) {
+        return this;
     }
 
     /**
@@ -693,8 +529,8 @@
      *  {@link #decodeDrawable}; attempting to decode an unpremultiplied
      *  {@link Drawable} will throw an {@link java.lang.IllegalStateException}.
      */
-    public void setRequireUnpremultiplied(boolean requireUnpremultiplied) {
-        mRequireUnpremultiplied = requireUnpremultiplied;
+    public ImageDecoder setRequireUnpremultiplied(boolean requireUnpremultiplied) {
+        return this;
     }
 
     /**
@@ -710,8 +546,8 @@
      *  {@link Canvas} will be recorded immediately and then applied to each
      *  frame.</p>
      */
-    public void setPostProcessor(@Nullable PostProcessor p) {
-        mPostProcessor = p;
+    public ImageDecoder setPostProcessor(@Nullable PostProcessor p) {
+        return this;
     }
 
     /**
@@ -720,8 +556,8 @@
      *  Will be called if there is an error in the input. Without one, a
      *  partial {@link Bitmap} will be created.
      */
-    public void setOnPartialImageListener(@Nullable OnPartialImageListener l) {
-        mOnPartialImageListener = l;
+    public ImageDecoder setOnPartialImageListener(@Nullable OnPartialImageListener l) {
+        return this;
     }
 
     /**
@@ -736,8 +572,21 @@
      *  {@link BitmapRegionDecoder#decodeRegion}. This supports all formats,
      *  but merely crops the output.</p>
      */
-    public void setCrop(@Nullable Rect subset) {
-        mCropRect = subset;
+    public ImageDecoder setCrop(@Nullable Rect subset) {
+        return this;
+    }
+
+    /**
+     *  Set a Rect for retrieving nine patch padding.
+     *
+     *  If the image is a nine patch, this Rect will be set to the padding
+     *  rectangle during decode. Otherwise it will not be modified.
+     *
+     *  @hide
+     */
+    public ImageDecoder setOutPaddingRect(@NonNull Rect outPadding) {
+        mOutPaddingRect = outPadding;
+        return this;
     }
 
     /**
@@ -756,8 +605,8 @@
      *  order to modify. Attempting to decode a mutable {@link Drawable} will
      *  throw an {@link java.lang.IllegalStateException}.</p>
      */
-    public void setMutable(boolean mutable) {
-        mMutable = mutable;
+    public ImageDecoder setMutable(boolean mutable) {
+        return this;
     }
 
     /**
@@ -768,8 +617,8 @@
      *  an opaque {@link Bitmap}, this may result in a {@link Bitmap.Config}
      *  with no alpha information.
      */
-    public void setPreferRamOverQuality(boolean preferRamOverQuality) {
-        mPreferRamOverQuality = preferRamOverQuality;
+    public ImageDecoder setPreferRamOverQuality(boolean preferRamOverQuality) {
+        return this;
     }
 
     /**
@@ -784,84 +633,12 @@
      *  {@link #decodeBitmap} throwing an
      *  {@link java.lang.IllegalStateException}.</p>
      */
-    public void setAsAlphaMask(boolean asAlphaMask) {
-        mAsAlphaMask = asAlphaMask;
+    public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
+        return this;
     }
 
     @Override
     public void close() {
-        mCloseGuard.close();
-        if (!mClosed.compareAndSet(false, true)) {
-            return;
-        }
-        nClose(mNativePtr);
-        mNativePtr = 0;
-
-        IoUtils.closeQuietly(mInputStream);
-        IoUtils.closeQuietly(mAssetFd);
-
-        mInputStream = null;
-        mAssetFd = null;
-        mTempStorage = null;
-    }
-
-    private void checkState() {
-        if (mNativePtr == 0) {
-            throw new IllegalStateException("Cannot use closed ImageDecoder!");
-        }
-
-        checkSubset(mDesiredWidth, mDesiredHeight, mCropRect);
-
-        if (mAllocator == ALLOCATOR_HARDWARE) {
-            if (mMutable) {
-                throw new IllegalStateException("Cannot make mutable HARDWARE Bitmap!");
-            }
-            if (mAsAlphaMask) {
-                throw new IllegalStateException("Cannot make HARDWARE Alpha mask Bitmap!");
-            }
-        }
-
-        if (mPostProcessor != null && mRequireUnpremultiplied) {
-            throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
-        }
-    }
-
-    private static void checkSubset(int width, int height, Rect r) {
-        if (r == null) {
-            return;
-        }
-        if (r.left < 0 || r.top < 0 || r.right > width || r.bottom > height) {
-            throw new IllegalStateException("Subset " + r + " not contained by "
-                    + "scaled image bounds: (" + width + " x " + height + ")");
-        }
-    }
-
-    @NonNull
-    private Bitmap decodeBitmap() throws IOException {
-        checkState();
-        // nDecodeBitmap calls onPartialImage only if mOnPartialImageListener
-        // exists
-        ImageDecoder partialImagePtr = mOnPartialImageListener == null ? null : this;
-        // nDecodeBitmap calls postProcessAndRelease only if mPostProcessor
-        // exists.
-        ImageDecoder postProcessPtr = mPostProcessor == null ? null : this;
-        return nDecodeBitmap(mNativePtr, partialImagePtr,
-                postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
-                mMutable, mAllocator, mRequireUnpremultiplied,
-                mPreferRamOverQuality, mAsAlphaMask);
-
-    }
-
-    private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
-            @NonNull Source src) {
-        if (listener != null) {
-            ImageInfo info = new ImageInfo(this);
-            try {
-                listener.onHeaderDecoded(this, info, src);
-            } finally {
-                info.mDecoder = null;
-            }
-        }
     }
 
     /**
@@ -879,57 +656,8 @@
     @NonNull
     public static Drawable decodeDrawable(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
-        try (ImageDecoder decoder = src.createImageDecoder()) {
-            decoder.mSource = src;
-            decoder.callHeaderDecoded(listener, src);
-
-            if (decoder.mRequireUnpremultiplied) {
-                // 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!");
-            }
-
-            if (decoder.mMutable) {
-                throw new IllegalStateException("Cannot decode a mutable " +
-                                                "Drawable!");
-            }
-
-            // this call potentially manipulates the decoder so it must be performed prior to
-            // decoding the bitmap and after decode set the density on the resulting bitmap
-            final int srcDensity = computeDensity(src, decoder);
-            if (decoder.mAnimated) {
-                // AnimatedImageDrawable calls postProcessAndRelease only if
-                // mPostProcessor exists.
-                ImageDecoder postProcessPtr = decoder.mPostProcessor == null ?
-                        null : decoder;
-                Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
-                        postProcessPtr, decoder.mDesiredWidth,
-                        decoder.mDesiredHeight, srcDensity,
-                        src.computeDstDensity(), decoder.mCropRect,
-                        decoder.mInputStream, decoder.mAssetFd);
-                // d has taken ownership of these objects.
-                decoder.mInputStream = null;
-                decoder.mAssetFd = null;
-                return d;
-            }
-
-            Bitmap bm = decoder.decodeBitmap();
-            bm.setDensity(srcDensity);
-
-            Resources res = src.getResources();
-            byte[] np = bm.getNinePatchChunk();
-            if (np != null && NinePatch.isNinePatchChunk(np)) {
-                Rect opticalInsets = new Rect();
-                bm.getOpticalInsets(opticalInsets);
-                Rect padding = new Rect();
-                nGetPadding(decoder.mNativePtr, padding);
-                return new NinePatchDrawable(res, bm, np, padding,
-                        opticalInsets, null);
-            }
-
-            return new BitmapDrawable(res, bm);
-        }
+        Bitmap bitmap = decodeBitmap(src, listener);
+        return new BitmapDrawable(src.getResources(), bitmap);
     }
 
     /**
@@ -956,55 +684,14 @@
     @NonNull
     public static Bitmap decodeBitmap(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
-        try (ImageDecoder decoder = src.createImageDecoder()) {
-            decoder.mSource = src;
-            decoder.callHeaderDecoded(listener, src);
-
-            // this call potentially manipulates the decoder so it must be performed prior to
-            // decoding the bitmap
-            final int srcDensity = computeDensity(src, decoder);
-            Bitmap bm = decoder.decodeBitmap();
-            bm.setDensity(srcDensity);
-            return bm;
+        TypedValue value = new TypedValue();
+        value.density = src.getDensity();
+        ImageDecoder decoder = src.createImageDecoder();
+        if (listener != null) {
+            listener.onHeaderDecoded(decoder, new ImageInfo(decoder), src);
         }
-    }
-
-    // This method may modify the decoder so it must be called prior to performing the decode
-    private static int computeDensity(@NonNull Source src, @NonNull ImageDecoder decoder) {
-        // if the caller changed the size then we treat the density as unknown
-        if (decoder.requestedResize()) {
-            return Bitmap.DENSITY_NONE;
-        }
-
-        // Special stuff for compatibility mode: if the target density is not
-        // the same as the display density, but the resource -is- the same as
-        // the display density, then don't scale it down to the target density.
-        // This allows us to load the system's density-correct resources into
-        // an application in compatibility mode, without scaling those down
-        // to the compatibility density only to have them scaled back up when
-        // drawn to the screen.
-        Resources res = src.getResources();
-        final int srcDensity = src.getDensity();
-        if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) {
-            return srcDensity;
-        }
-
-        // downscale the bitmap if the asset has a higher density than the default
-        final int dstDensity = src.computeDstDensity();
-        if (srcDensity != Bitmap.DENSITY_NONE && srcDensity > dstDensity) {
-            float scale = (float) dstDensity / srcDensity;
-            int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
-            int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
-            decoder.setResize(scaledWidth, scaledHeight);
-            return dstDensity;
-        }
-
-        return srcDensity;
-    }
-
-    @NonNull
-    private String getMimeType() {
-        return nGetMimeType(mNativePtr);
+        return BitmapFactory.decodeResourceStream(src.getResources(), value,
+                ((InputStreamSource) src).mInputStream, decoder.mOutPaddingRect, null);
     }
 
     /**
@@ -1014,48 +701,4 @@
     public static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
         return decodeBitmap(src, null);
     }
-
-    /**
-     * Private method called by JNI.
-     */
-    @SuppressWarnings("unused")
-    private int postProcessAndRelease(@NonNull Canvas canvas) {
-        try {
-            return mPostProcessor.onPostProcess(canvas);
-        } finally {
-            canvas.release();
-        }
-    }
-
-    /**
-     * Private method called by JNI.
-     */
-    @SuppressWarnings("unused")
-    private boolean onPartialImage(@Error int error) {
-        return mOnPartialImageListener.onPartialImage(error, mSource);
-    }
-
-    private static native ImageDecoder nCreate(long asset) throws IOException;
-    private static native ImageDecoder nCreate(ByteBuffer buffer,
-                                               int position,
-                                               int limit) throws IOException;
-    private static native ImageDecoder nCreate(byte[] data, int offset,
-                                               int length) throws IOException;
-    private static native ImageDecoder nCreate(InputStream is, byte[] storage);
-    // The fd must be seekable.
-    private static native ImageDecoder nCreate(FileDescriptor fd) throws IOException;
-    @NonNull
-    private static native Bitmap nDecodeBitmap(long nativePtr,
-            @Nullable ImageDecoder partialImageListener,
-            @Nullable ImageDecoder postProcessor,
-            int width, int height,
-            @Nullable Rect cropRect, boolean mutable,
-            int allocator, boolean requireUnpremul,
-            boolean preferRamOverQuality, boolean asAlphaMask)
-        throws IOException;
-    private static native Size nGetSampledSize(long nativePtr,
-                                               int sampleSize);
-    private static native void nGetPadding(long nativePtr, @NonNull Rect outRect);
-    private static native void nClose(long nativePtr);
-    private static native String nGetMimeType(long nativePtr);
 }
diff --git a/android/graphics/ImageDecoder_Delegate.java b/android/graphics/ImageDecoder_Delegate.java
deleted file mode 100644
index d9fe9bf..0000000
--- a/android/graphics/ImageDecoder_Delegate.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2018 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 android.graphics;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.ImageDecoder.InputStreamSource;
-import android.graphics.ImageDecoder.OnHeaderDecodedListener;
-import android.graphics.ImageDecoder.Source;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.TypedValue;
-
-import java.io.IOException;
-
-public class ImageDecoder_Delegate {
-    @LayoutlibDelegate
-    static Bitmap decodeBitmap(@NonNull Source src, @Nullable OnHeaderDecodedListener listener)
-            throws IOException {
-        TypedValue value = new TypedValue();
-        value.density = src.getDensity();
-        return BitmapFactory.decodeResourceStream(src.getResources(), value,
-                ((InputStreamSource) src).mInputStream, null, null);
-    }
-
-    @LayoutlibDelegate
-    static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
-        return decodeBitmap(src, null);
-    }
-
-    @LayoutlibDelegate
-    static Bitmap decodeBitmap(ImageDecoder thisDecoder) {
-        return null;
-    }
-
-    @LayoutlibDelegate
-    static Drawable decodeDrawable(@NonNull Source src, @Nullable OnHeaderDecodedListener listener)
-            throws IOException {
-        Bitmap bitmap = decodeBitmap(src, listener);
-        return new BitmapDrawable(src.getResources(), bitmap);
-    }
-
-    @LayoutlibDelegate
-    static Drawable decodeDrawable(@NonNull Source src) throws IOException {
-        return decodeDrawable(src, null);
-    }
-}
diff --git a/android/graphics/Matrix_Delegate.java b/android/graphics/Matrix_Delegate.java
index 354f919..5ae181d 100644
--- a/android/graphics/Matrix_Delegate.java
+++ b/android/graphics/Matrix_Delegate.java
@@ -118,8 +118,7 @@
         return true;
     }
 
-    public static float[] makeValues(AffineTransform matrix) {
-        float[] values = new float[MATRIX_SIZE];
+    private static float[] setValues(AffineTransform matrix, float[] values) {
         values[0] = (float) matrix.getScaleX();
         values[1] = (float) matrix.getShearX();
         values[2] = (float) matrix.getTranslateX();
@@ -133,6 +132,10 @@
         return values;
     }
 
+    public static float[] makeValues(AffineTransform matrix) {
+        return setValues(matrix, new float[MATRIX_SIZE]);
+    }
+
     public static Matrix_Delegate make(AffineTransform matrix) {
         return new Matrix_Delegate(makeValues(matrix));
     }
@@ -616,12 +619,7 @@
         try {
             AffineTransform affineTransform = d.getAffineTransform();
             AffineTransform inverseTransform = affineTransform.createInverse();
-            inv_mtx.mValues[0] = (float)inverseTransform.getScaleX();
-            inv_mtx.mValues[1] = (float)inverseTransform.getShearX();
-            inv_mtx.mValues[2] = (float)inverseTransform.getTranslateX();
-            inv_mtx.mValues[3] = (float)inverseTransform.getScaleX();
-            inv_mtx.mValues[4] = (float)inverseTransform.getShearY();
-            inv_mtx.mValues[5] = (float)inverseTransform.getTranslateY();
+            setValues(inverseTransform, inv_mtx.mValues);
 
             return true;
         } catch (NoninvertibleTransformException e) {
diff --git a/android/graphics/Movie.java b/android/graphics/Movie.java
index c8f86c6..83857be 100644
--- a/android/graphics/Movie.java
+++ b/android/graphics/Movie.java
@@ -17,9 +17,13 @@
 package android.graphics;
 
 import android.content.res.AssetManager;
-import java.io.InputStream;
-import java.io.FileInputStream;
 
+import java.io.FileInputStream;
+import java.io.InputStream;
+
+/**
+ * @deprecated Prefer {@link android.graphics.drawable.AnimatedImageDrawable}.
+ */
 public class Movie {
     private long mNativeMovie;
 
diff --git a/android/graphics/Paint.java b/android/graphics/Paint.java
index 5a80ee2..42dac38 100644
--- a/android/graphics/Paint.java
+++ b/android/graphics/Paint.java
@@ -19,10 +19,8 @@
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Size;
-import android.graphics.FontListParser;
 import android.graphics.fonts.FontVariationAxis;
 import android.os.LocaleList;
-import android.text.FontConfig;
 import android.text.GraphicsOperations;
 import android.text.SpannableString;
 import android.text.SpannedString;
@@ -33,14 +31,13 @@
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Locale;
 
-import libcore.util.NativeAllocationRegistry;
-
 /**
  * The Paint class holds the style and color information about how to draw
  * geometries, text and bitmaps.
@@ -2838,6 +2835,16 @@
         return result;
     }
 
+    /**
+     * Returns true of the passed {@link Paint} will have the same effect on text measurement
+     *
+     * @param other A {@link Paint} object.
+     * @return true if the other {@link Paint} has the same effect on text measurement.
+     */
+    public boolean equalsForTextMeasurement(@NonNull Paint other) {
+        return nEqualsForTextMeasurement(mNativePaint, other.mNativePaint);
+    }
+
     // regular JNI
     private static native long nGetNativeFinalizer();
     private static native long nInit();
@@ -3005,4 +3012,6 @@
     private static native float nGetStrikeThruThickness(long paintPtr);
     @CriticalNative
     private static native void nSetTextSize(long paintPtr, float textSize);
+    @CriticalNative
+    private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr);
 }
diff --git a/android/graphics/Paint_Delegate.java b/android/graphics/Paint_Delegate.java
index 4f05176..dae966d 100644
--- a/android/graphics/Paint_Delegate.java
+++ b/android/graphics/Paint_Delegate.java
@@ -1197,6 +1197,11 @@
         return (1.0f / 18.0f) * nGetTextSize(paintPtr);
     }
 
+    @LayoutlibDelegate
+    /*package*/ static boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr) {
+        return leftPaintPtr == rightPaintPtr;
+    }
+
     // ---- Private delegate/helper methods ----
 
     /*package*/ Paint_Delegate() {
diff --git a/android/graphics/Path.java b/android/graphics/Path.java
index 098cdc6..cd0862c 100644
--- a/android/graphics/Path.java
+++ b/android/graphics/Path.java
@@ -24,6 +24,8 @@
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
+import libcore.util.NativeAllocationRegistry;
+
 /**
  * The Path class encapsulates compound (multiple contour) geometric paths
  * consisting of straight line segments, quadratic curves, and cubic curves.
@@ -32,10 +34,14 @@
  * text on a path.
  */
 public class Path {
+
+    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+                Path.class.getClassLoader(), nGetFinalizer(), 48 /* dummy size */);
+
     /**
      * @hide
      */
-    public long mNativePath;
+    public final long mNativePath;
 
     /**
      * @hide
@@ -52,6 +58,7 @@
      */
     public Path() {
         mNativePath = nInit();
+        sRegistry.registerNativeAllocation(this, mNativePath);
     }
 
     /**
@@ -69,6 +76,7 @@
             }
         }
         mNativePath = nInit(valNative);
+        sRegistry.registerNativeAllocation(this, mNativePath);
     }
 
     /**
@@ -297,7 +305,7 @@
      *             a rectangle
      * @return     true if the path specifies a rectangle
      */
-    public boolean isRect(RectF rect) {
+    public boolean isRect(@Nullable  RectF rect) {
         return nIsRect(mNativePath, rect);
     }
 
@@ -771,15 +779,6 @@
         nTransform(mNativePath, matrix.native_instance);
     }
 
-    protected void finalize() throws Throwable {
-        try {
-            nFinalize(mNativePath);
-            mNativePath = 0;  //  Other finalizers can still call us.
-        } finally {
-            super.finalize();
-        }
-    }
-
     /** @hide */
     public final long readOnlyNI() {
         return mNativePath;
@@ -820,7 +819,7 @@
 
     private static native long nInit();
     private static native long nInit(long nPath);
-    private static native void nFinalize(long nPath);
+    private static native long nGetFinalizer();
     private static native void nSet(long native_dst, long nSrc);
     private static native void nComputeBounds(long nPath, RectF bounds);
     private static native void nIncReserve(long nPath, int extraPtCount);
diff --git a/android/graphics/Picture.java b/android/graphics/Picture.java
index 08eeaff..d01ff6f 100644
--- a/android/graphics/Picture.java
+++ b/android/graphics/Picture.java
@@ -31,8 +31,9 @@
  * be replayed on a hardware accelerated canvas.</p>
  */
 public class Picture {
-    private Canvas mRecordingCanvas;
+    private PictureCanvas mRecordingCanvas;
     private long mNativePicture;
+    private boolean mRequiresHwAcceleration;
 
     private static final int WORKING_STREAM_STORAGE = 16 * 1024;
 
@@ -78,8 +79,12 @@
      * into it.
      */
     public Canvas beginRecording(int width, int height) {
+        if (mRecordingCanvas != null) {
+            throw new IllegalStateException("Picture already recording, must call #endRecording()");
+        }
         long ni = nativeBeginRecording(mNativePicture, width, height);
-        mRecordingCanvas = new RecordingCanvas(this, ni);
+        mRecordingCanvas = new PictureCanvas(this, ni);
+        mRequiresHwAcceleration = false;
         return mRecordingCanvas;
     }
 
@@ -91,6 +96,7 @@
      */
     public void endRecording() {
         if (mRecordingCanvas != null) {
+            mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
             mRecordingCanvas = null;
             nativeEndRecording(mNativePicture);
         }
@@ -113,6 +119,18 @@
     }
 
     /**
+     * Indicates whether or not this Picture contains recorded commands that only work when
+     * drawn to a hardware-accelerated canvas. If this returns true then this Picture can only
+     * be drawn to another Picture or to a Canvas where canvas.isHardwareAccelerated() is true.
+     *
+     * @return true if the Picture can only be drawn to a hardware-accelerated canvas,
+     *         false otherwise.
+     */
+    public boolean requiresHardwareAcceleration() {
+        return mRequiresHwAcceleration;
+    }
+
+    /**
      * Draw this picture on the canvas.
      * <p>
      * Prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this call could
@@ -129,6 +147,9 @@
         if (mRecordingCanvas != null) {
             endRecording();
         }
+        if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) {
+            canvas.onHwBitmapInSwMode();
+        }
         nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
     }
 
@@ -164,8 +185,7 @@
         if (stream == null) {
             throw new NullPointerException();
         }
-        if (!nativeWriteToStream(mNativePicture, stream,
-                             new byte[WORKING_STREAM_STORAGE])) {
+        if (!nativeWriteToStream(mNativePicture, stream, new byte[WORKING_STREAM_STORAGE])) {
             throw new RuntimeException();
         }
     }
@@ -182,12 +202,15 @@
                                            OutputStream stream, byte[] storage);
     private static native void nativeDestructor(long nativePicture);
 
-    private static class RecordingCanvas extends Canvas {
+    private static class PictureCanvas extends Canvas {
         private final Picture mPicture;
+        boolean mHoldsHwBitmap;
 
-        public RecordingCanvas(Picture pict, long nativeCanvas) {
+        public PictureCanvas(Picture pict, long nativeCanvas) {
             super(nativeCanvas);
             mPicture = pict;
+            // Disable bitmap density scaling. This matches DisplayListCanvas.
+            mDensity = 0;
         }
 
         @Override
@@ -202,5 +225,10 @@
             }
             super.drawPicture(picture);
         }
+
+        @Override
+        protected void onHwBitmapInSwMode() {
+            mHoldsHwBitmap = true;
+        }
     }
 }
diff --git a/android/graphics/SurfaceTexture.java b/android/graphics/SurfaceTexture.java
index 97edf22..1eebd26 100644
--- a/android/graphics/SurfaceTexture.java
+++ b/android/graphics/SurfaceTexture.java
@@ -318,13 +318,17 @@
      * Retrieve the timestamp associated with the texture image set by the most recent call to
      * updateTexImage.
      *
-     * This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp
-     * should be unaffected by time-of-day adjustments, and for a camera should be strictly
-     * monotonic but for a MediaPlayer may be reset when the position is set.  The
-     * specific meaning and zero point of the timestamp depends on the source providing images to
-     * the SurfaceTexture. Unless otherwise specified by the image source, timestamps cannot
-     * generally be compared across SurfaceTexture instances, or across multiple program
-     * invocations. It is mostly useful for determining time offsets between subsequent frames.
+     * <p>This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp
+     * should be unaffected by time-of-day adjustments. The specific meaning and zero point of the
+     * timestamp depends on the source providing images to the SurfaceTexture. Unless otherwise
+     * specified by the image source, timestamps cannot generally be compared across SurfaceTexture
+     * instances, or across multiple program invocations. It is mostly useful for determining time
+     * offsets between subsequent frames.</p>
+     *
+     * <p>For camera sources, timestamps should be strictly monotonic. Timestamps from MediaPlayer
+     * sources may be reset when the playback position is set. For EGL and Vulkan producers, the
+     * timestamp is the desired present time set with the EGL_ANDROID_presentation_time or
+     * VK_GOOGLE_display_timing extensions.</p>
      */
 
     public long getTimestamp() {
diff --git a/android/graphics/Typeface.java b/android/graphics/Typeface.java
index ef41507..20c22b7 100644
--- a/android/graphics/Typeface.java
+++ b/android/graphics/Typeface.java
@@ -16,25 +16,18 @@
 
 package android.graphics;
 
-import static android.content.res.FontResourcesParser.ProviderResourceEntry;
-import static android.content.res.FontResourcesParser.FontFileResourceEntry;
-import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry;
 import static android.content.res.FontResourcesParser.FamilyResourceEntry;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry;
+import static android.content.res.FontResourcesParser.FontFileResourceEntry;
+import static android.content.res.FontResourcesParser.ProviderResourceEntry;
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.AssetManager;
-import android.graphics.FontListParser;
 import android.graphics.fonts.FontVariationAxis;
 import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.ParcelFileDescriptor;
-import android.os.ResultReceiver;
 import android.provider.FontRequest;
 import android.provider.FontsContract;
 import android.text.FontConfig;
@@ -49,7 +42,9 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
-import libcore.io.IoUtils;
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -58,18 +53,17 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
-import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * The Typeface class specifies the typeface and intrinsic style of a font.
@@ -81,6 +75,9 @@
 
     private static String TAG = "Typeface";
 
+    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+            Typeface.class.getClassLoader(), nativeGetReleaseFunc(), 64);
+
     /** The default NORMAL typeface object */
     public static final Typeface DEFAULT;
     /**
@@ -130,6 +127,11 @@
      */
     public long native_instance;
 
+    /** @hide */
+    @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Style {}
+
     // Style
     public static final int NORMAL = 0;
     public static final int BOLD = 1;
@@ -137,8 +139,15 @@
     public static final int BOLD_ITALIC = 3;
     /** @hide */ public static final int STYLE_MASK = 0x03;
 
-    private int mStyle = 0;
-    private int mWeight = 0;
+    private @Style int mStyle = 0;
+
+    /**
+     * A maximum value for the weight value.
+     * @hide
+     */
+    public static final int MAX_WEIGHT = 1000;
+
+    private @IntRange(from = 0, to = MAX_WEIGHT) int mWeight = 0;
 
     // Value for weight and italic. Indicates the value is resolved by font metadata.
     // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
@@ -158,15 +167,13 @@
         nativeSetDefault(t.native_instance);
     }
 
-    // TODO: Make this public API. (b/64852739)
-    /** @hide */
-    @VisibleForTesting
-    public int getWeight() {
+    /** Returns the typeface's weight value */
+    public @IntRange(from = 0, to = 1000) int getWeight() {
         return mWeight;
     }
 
     /** Returns the typeface's intrinsic style attributes */
-    public int getStyle() {
+    public @Style int getStyle() {
         return mStyle;
     }
 
@@ -394,7 +401,7 @@
          * weight and italic information, so {@link #setWeight} and {@link #setItalic} are used
          * for style matching during font selection.
          *
-         * @param results The array of {@link FontsContract.FontInfo}
+         * @param fonts The array of {@link FontsContract.FontInfo}
          * @param buffers The mapping from URI to buffers to be used during building.
          * @hide
          */
@@ -672,7 +679,7 @@
      *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
      * @return The best matching typeface.
      */
-    public static Typeface create(String familyName, int style) {
+    public static Typeface create(String familyName, @Style int style) {
         return create(sSystemFontMap.get(familyName), style);
     }
 
@@ -693,7 +700,7 @@
      *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
      * @return The best matching typeface.
      */
-    public static Typeface create(Typeface family, int style) {
+    public static Typeface create(Typeface family, @Style int style) {
         if ((style & ~STYLE_MASK) != 0) {
             style = NORMAL;
         }
@@ -789,7 +796,7 @@
      *
      * @return the default typeface that corresponds to the style
      */
-    public static Typeface defaultFromStyle(int style) {
+    public static Typeface defaultFromStyle(@Style int style) {
         return sDefaults[style];
     }
 
@@ -801,39 +808,18 @@
      * @return The new typeface.
      */
     public static Typeface createFromAsset(AssetManager mgr, String path) {
-        if (path == null) {
-            throw new NullPointerException();  // for backward compatibility
-        }
-        synchronized (sDynamicCacheLock) {
-            Typeface typeface = new Builder(mgr, path).build();
-            if (typeface != null) return typeface;
+        Preconditions.checkNotNull(path); // for backward compatibility
+        Preconditions.checkNotNull(mgr);
 
-            final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
-                    null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
-                    DEFAULT_FAMILY);
-            typeface = sDynamicTypefaceCache.get(key);
-            if (typeface != null) return typeface;
-
-            final FontFamily fontFamily = new FontFamily();
-            if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
-                    0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
-                    null /* axes */)) {
-                // Due to backward compatibility, even if the font is not supported by our font
-                // stack, we need to place the empty font at the first place. The typeface with
-                // empty font behaves different from default typeface especially in fallback
-                // font selection.
-                fontFamily.allowUnsupportedFont();
-                fontFamily.freeze();
-                final FontFamily[] families = { fontFamily };
-                typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
-                        RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
-                sDynamicTypefaceCache.put(key, typeface);
-                return typeface;
-            } else {
-                fontFamily.abortCreation();
-            }
+        Typeface typeface = new Builder(mgr, path).build();
+        if (typeface != null) return typeface;
+        // check if the file exists, and throw an exception for backward compatibility
+        try (InputStream inputStream = mgr.open(path)) {
+        } catch (IOException e) {
+            throw new RuntimeException("Font asset not found " + path);
         }
-        throw new RuntimeException("Font asset not found " + path);
+
+        return Typeface.DEFAULT;
     }
 
     /**
@@ -851,13 +837,22 @@
     /**
      * Create a new typeface from the specified font file.
      *
-     * @param path The path to the font data.
+     * @param file The path to the font data.
      * @return The new typeface.
      */
-    public static Typeface createFromFile(@Nullable File path) {
+    public static Typeface createFromFile(@Nullable File file) {
         // For the compatibility reasons, leaving possible NPE here.
         // See android.graphics.cts.TypefaceTest#testCreateFromFileByFileReferenceNull
-        return createFromFile(path.getAbsolutePath());
+
+        Typeface typeface = new Builder(file).build();
+        if (typeface != null) return typeface;
+
+        // check if the file exists, and throw an exception for backward compatibility
+        if (!file.exists()) {
+            throw new RuntimeException("Font asset not found " + file.getAbsolutePath());
+        }
+
+        return Typeface.DEFAULT;
     }
 
     /**
@@ -867,22 +862,8 @@
      * @return The new typeface.
      */
     public static Typeface createFromFile(@Nullable String path) {
-        final FontFamily fontFamily = new FontFamily();
-        if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
-                  RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
-            // Due to backward compatibility, even if the font is not supported by our font
-            // stack, we need to place the empty font at the first place. The typeface with
-            // empty font behaves different from default typeface especially in fallback font
-            // selection.
-            fontFamily.allowUnsupportedFont();
-            fontFamily.freeze();
-            FontFamily[] families = { fontFamily };
-            return createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
-                    RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
-        } else {
-            fontFamily.abortCreation();
-        }
-        throw new RuntimeException("Font not found " + path);
+        Preconditions.checkNotNull(path); // for backward compatibility
+        return createFromFile(new File(path));
     }
 
     /**
@@ -900,18 +881,25 @@
     }
 
     /**
+     * This method is used by supportlib-v27.
+     * TODO: Remove private API use in supportlib: http://b/72665240
+     */
+    private static Typeface createFromFamiliesWithDefault(FontFamily[] families, int weight,
+                int italic) {
+        return createFromFamiliesWithDefault(families, DEFAULT_FAMILY, weight, italic);
+    }
+
+    /**
      * Create a new typeface from an array of font families, including
      * also the font families in the fallback list.
      * @param fallbackName the family name. If given families don't support characters, the
      *               characters will be rendered with this family.
-     * @param weight the weight for this family. {@link RESOLVE_BY_FONT_TABLE} can be used. In that
-     *               case, the table information in the first family's font is used. If the first
-     *               family has multiple fonts, the closest to the regular weight and upright font
-     *               is used.
-     * @param italic the italic information for this family. {@link RESOLVE_BY_FONT_TABLE} can be
-     *               used. In that case, the table information in the first family's font is used.
-     *               If the first family has multiple fonts, the closest to the regular weight and
-     *               upright font is used.
+     * @param weight the weight for this family. In that case, the table information in the first
+     *               family's font is used. If the first family has multiple fonts, the closest to
+     *               the regular weight and upright font is used.
+     * @param italic the italic information for this family. In that case, the table information in
+     *               the first family's font is used. If the first family has multiple fonts, the
+     *               closest to the regular weight and upright font is used.
      * @param families array of font families
      */
     private static Typeface createFromFamiliesWithDefault(FontFamily[] families,
@@ -937,6 +925,7 @@
         }
 
         native_instance = ni;
+        sRegistry.registerNativeAllocation(this, native_instance);
         mStyle = nativeGetStyle(ni);
         mWeight = nativeGetWeight(ni);
     }
@@ -1146,16 +1135,6 @@
     }
 
     @Override
-    protected void finalize() throws Throwable {
-        try {
-            nativeUnref(native_instance);
-            native_instance = 0;  // Other finalizers can still call us.
-        } finally {
-            super.finalize();
-        }
-    }
-
-    @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
@@ -1199,10 +1178,18 @@
     private static native long nativeCreateFromTypefaceWithVariation(
             long native_instance, List<FontVariationAxis> axes);
     private static native long nativeCreateWeightAlias(long native_instance, int weight);
-    private static native void nativeUnref(long native_instance);
-    private static native int  nativeGetStyle(long native_instance);
-    private static native int  nativeGetWeight(long native_instance);
     private static native long nativeCreateFromArray(long[] familyArray, int weight, int italic);
-    private static native void nativeSetDefault(long native_instance);
     private static native int[] nativeGetSupportedAxes(long native_instance);
+
+    @CriticalNative
+    private static native void nativeSetDefault(long nativePtr);
+
+    @CriticalNative
+    private static native int  nativeGetStyle(long nativePtr);
+
+    @CriticalNative
+    private static native int  nativeGetWeight(long nativePtr);
+
+    @CriticalNative
+    private static native long nativeGetReleaseFunc();
 }
diff --git a/android/graphics/drawable/AdaptiveIconDrawable.java b/android/graphics/drawable/AdaptiveIconDrawable.java
index 1d0cfa5..fdd638a 100644
--- a/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -44,6 +44,7 @@
 import android.util.PathParser;
 
 import com.android.internal.R;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -668,13 +669,7 @@
 
     @Override
     public void setAlpha(int alpha) {
-        final ChildDrawable[] array = mLayerState.mChildren;
-        for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
-            final Drawable dr = array[i].mDrawable;
-            if (dr != null) {
-                dr.setAlpha(alpha);
-            }
-        }
+        mPaint.setAlpha(alpha);
     }
 
     @Override
diff --git a/android/graphics/drawable/AnimatedImageDrawable.java b/android/graphics/drawable/AnimatedImageDrawable.java
index 3034a10..898939e 100644
--- a/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/android/graphics/drawable/AnimatedImageDrawable.java
@@ -20,39 +20,266 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.ImageDecoder;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.os.SystemClock;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AttributeSet;
 import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
 
-import libcore.io.IoUtils;
+import com.android.internal.R;
+
+import dalvik.annotation.optimization.FastNative;
+
 import libcore.util.NativeAllocationRegistry;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.Runnable;
+import java.util.ArrayList;
 
 /**
- * @hide
+ * {@link Drawable} for drawing animated images (like GIF).
+ *
+ * <p>The framework handles decoding subsequent frames in another thread and
+ * updating when necessary. The drawable will only animate while it is being
+ * displayed.</p>
+ *
+ * <p>Created by {@link ImageDecoder#decodeDrawable}. A user needs to call
+ * {@link #start} to start the animation.</p>
+ *
+ * <p>It can also be defined in XML using the <code>&lt;animated-image></code>
+ * element.</p>
+ *
+ * @attr ref android.R.styleable#AnimatedImageDrawable_src
+ * @attr ref android.R.styleable#AnimatedImageDrawable_autoStart
+ * @attr ref android.R.styleable#AnimatedImageDrawable_repeatCount
+ * @attr ref android.R.styleable#AnimatedImageDrawable_autoMirrored
  */
-public class AnimatedImageDrawable extends Drawable implements Animatable {
-    private final long                mNativePtr;
-    private final InputStream         mInputStream;
-    private final AssetFileDescriptor mAssetFd;
+public class AnimatedImageDrawable extends Drawable implements Animatable2 {
+    private int mIntrinsicWidth;
+    private int mIntrinsicHeight;
 
-    private final int                 mIntrinsicWidth;
-    private final int                 mIntrinsicHeight;
+    private boolean mStarting;
 
-    private Runnable mRunnable = new Runnable() {
-        @Override
-        public void run() {
-            invalidateSelf();
+    private Handler mHandler;
+
+    private class State {
+        State(long nativePtr, InputStream is, AssetFileDescriptor afd) {
+            mNativePtr = nativePtr;
+            mInputStream = is;
+            mAssetFd = afd;
         }
-    };
+
+        final long mNativePtr;
+
+        // These just keep references so the native code can continue using them.
+        private final InputStream mInputStream;
+        private final AssetFileDescriptor mAssetFd;
+
+        int[] mThemeAttrs = null;
+        boolean mAutoMirrored = false;
+        int mRepeatCount = REPEAT_UNDEFINED;
+    }
+
+    private State mState;
+
+    private Runnable mRunnable;
+
+    private ColorFilter mColorFilter;
+
+    /**
+     *  Pass this to {@link #setRepeatCount} to repeat infinitely.
+     *
+     *  <p>{@link Animatable2.AnimationCallback#onAnimationEnd} will never be
+     *  called unless there is an error.</p>
+     */
+    public static final int REPEAT_INFINITE = -1;
+
+    /** @removed
+     * @deprecated Replaced with REPEAT_INFINITE to match other APIs.
+     */
+    @java.lang.Deprecated
+    public static final int LOOP_INFINITE = REPEAT_INFINITE;
+
+    private static final int REPEAT_UNDEFINED = -2;
+
+    /**
+     *  Specify the number of times to repeat the animation.
+     *
+     *  <p>By default, the repeat count in the encoded data is respected. If set
+     *  to {@link #REPEAT_INFINITE}, the animation will repeat as long as it is
+     *  displayed. If the value is {@code 0}, the animation will play once.</p>
+     *
+     *  <p>This call replaces the current repeat count. If the encoded data
+     *  specified a repeat count of {@code 2} (meaning that
+     *  {@link #getRepeatCount()} returns {@code 2}, the animation will play
+     *  three times. Calling {@code setRepeatCount(1)} will result in playing only
+     *  twice and {@link #getRepeatCount()} returning {@code 1}.</p>
+     *
+     *  <p>If the animation is already playing, the iterations that have already
+     *  occurred count towards the new count. If the animation has already
+     *  repeated the appropriate number of times (or more), it will finish its
+     *  current iteration and then stop.</p>
+     */
+    public void setRepeatCount(@IntRange(from = REPEAT_INFINITE) int repeatCount) {
+        if (repeatCount < REPEAT_INFINITE) {
+            throw new IllegalArgumentException("invalid value passed to setRepeatCount"
+                    + repeatCount);
+        }
+        if (mState.mRepeatCount != repeatCount) {
+            mState.mRepeatCount = repeatCount;
+            if (mState.mNativePtr != 0) {
+                nSetRepeatCount(mState.mNativePtr, repeatCount);
+            }
+        }
+    }
+
+    /** @removed
+     * @deprecated Replaced with setRepeatCount to match other APIs.
+     */
+    @java.lang.Deprecated
+    public void setLoopCount(int loopCount) {
+        setRepeatCount(loopCount);
+    }
+
+    /**
+     *  Retrieve the number of times the animation will repeat.
+     *
+     *  <p>By default, the repeat count in the encoded data is respected. If the
+     *  value is {@link #REPEAT_INFINITE}, the animation will repeat as long as
+     *  it is displayed. If the value is {@code 0}, it will play once.</p>
+     *
+     *  <p>Calling {@link #setRepeatCount} will make future calls to this method
+     *  return the value passed to {@link #setRepeatCount}.</p>
+     */
+    public int getRepeatCount() {
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called getRepeatCount on empty AnimatedImageDrawable");
+        }
+        if (mState.mRepeatCount == REPEAT_UNDEFINED) {
+            mState.mRepeatCount = nGetRepeatCount(mState.mNativePtr);
+
+        }
+        return mState.mRepeatCount;
+    }
+
+    /** @removed
+     * @deprecated Replaced with getRepeatCount to match other APIs.
+     */
+    @java.lang.Deprecated
+    public int getLoopCount(int loopCount) {
+        return getRepeatCount();
+    }
+
+    /**
+     * Create an empty AnimatedImageDrawable.
+     */
+    public AnimatedImageDrawable() {
+        mState = new State(0, null, null);
+    }
+
+    @Override
+    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+            throws XmlPullParserException, IOException {
+        super.inflate(r, parser, attrs, theme);
+
+        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimatedImageDrawable);
+        updateStateFromTypedArray(a, mSrcDensityOverride);
+    }
+
+    private void updateStateFromTypedArray(TypedArray a, int srcDensityOverride)
+            throws XmlPullParserException {
+        State oldState = mState;
+        final Resources r = a.getResources();
+        final int srcResId = a.getResourceId(R.styleable.AnimatedImageDrawable_src, 0);
+        if (srcResId != 0) {
+            // Follow the density handling in BitmapDrawable.
+            final TypedValue value = new TypedValue();
+            r.getValueForDensity(srcResId, srcDensityOverride, value, true);
+            if (srcDensityOverride > 0 && value.density > 0
+                    && value.density != TypedValue.DENSITY_NONE) {
+                if (value.density == srcDensityOverride) {
+                    value.density = r.getDisplayMetrics().densityDpi;
+                } else {
+                    value.density =
+                            (value.density * r.getDisplayMetrics().densityDpi) / srcDensityOverride;
+                }
+            }
+
+            int density = Bitmap.DENSITY_NONE;
+            if (value.density == TypedValue.DENSITY_DEFAULT) {
+                density = DisplayMetrics.DENSITY_DEFAULT;
+            } else if (value.density != TypedValue.DENSITY_NONE) {
+                density = value.density;
+            }
+
+            Drawable drawable = null;
+            try {
+                InputStream is = r.openRawResource(srcResId, value);
+                ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+                drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
+                    if (!info.isAnimated()) {
+                        throw new IllegalArgumentException("image is not animated");
+                    }
+                });
+            } catch (IOException e) {
+                throw new XmlPullParserException(a.getPositionDescription() +
+                        ": <animated-image> requires a valid 'src' attribute", null, e);
+            }
+
+            if (!(drawable instanceof AnimatedImageDrawable)) {
+                throw new XmlPullParserException(a.getPositionDescription() +
+                        ": <animated-image> did not decode animated");
+            }
+
+            // This may have previously been set without a src if we were waiting for a
+            // theme.
+            final int repeatCount = mState.mRepeatCount;
+            // Transfer the state of other to this one. other will be discarded.
+            AnimatedImageDrawable other = (AnimatedImageDrawable) drawable;
+            mState = other.mState;
+            other.mState = null;
+            mIntrinsicWidth =  other.mIntrinsicWidth;
+            mIntrinsicHeight = other.mIntrinsicHeight;
+            if (repeatCount != REPEAT_UNDEFINED) {
+                this.setRepeatCount(repeatCount);
+            }
+        }
+
+        mState.mThemeAttrs = a.extractThemeAttrs();
+        if (mState.mNativePtr == 0 && (mState.mThemeAttrs == null
+                || mState.mThemeAttrs[R.styleable.AnimatedImageDrawable_src] == 0)) {
+            throw new XmlPullParserException(a.getPositionDescription() +
+                    ": <animated-image> requires a valid 'src' attribute");
+        }
+
+        mState.mAutoMirrored = a.getBoolean(
+                R.styleable.AnimatedImageDrawable_autoMirrored, oldState.mAutoMirrored);
+
+        int repeatCount = a.getInt(
+                R.styleable.AnimatedImageDrawable_repeatCount, REPEAT_UNDEFINED);
+        if (repeatCount != REPEAT_UNDEFINED) {
+            this.setRepeatCount(repeatCount);
+        }
+
+        boolean autoStart = a.getBoolean(
+                R.styleable.AnimatedImageDrawable_autoStart, false);
+        if (autoStart && mState.mNativePtr != 0) {
+            this.start();
+        }
+    }
 
     /**
      * @hide
@@ -80,30 +307,13 @@
             mIntrinsicHeight = cropRect.height();
         }
 
-        mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect);
-        mInputStream = inputStream;
-        mAssetFd = afd;
+        mState = new State(nCreate(nativeImageDecoder, decoder, width, height, cropRect),
+                inputStream, afd);
 
-        // FIXME: Use the right size for the native allocation.
-        long nativeSize = 200;
+        final long nativeSize = nNativeByteSize(mState.mNativePtr);
         NativeAllocationRegistry registry = new NativeAllocationRegistry(
                 AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize);
-        registry.registerNativeAllocation(this, mNativePtr);
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        // FIXME: It's a shame that we have *both* a native finalizer and a Java
-        // one. The native one is necessary to report how much memory is being
-        // used natively, and this one is necessary to close the input. An
-        // alternative might be to read the entire stream ahead of time, so we
-        // can eliminate the Java finalizer.
-        try {
-            IoUtils.closeQuietly(mInputStream);
-            IoUtils.closeQuietly(mAssetFd);
-        } finally {
-            super.finalize();
-        }
+        registry.registerNativeAllocation(mState, mState.mNativePtr);
     }
 
     @Override
@@ -116,36 +326,76 @@
         return mIntrinsicHeight;
     }
 
+    // nDraw returns -1 if the animation has finished.
+    private static final int FINISHED = -1;
+
     @Override
     public void draw(@NonNull Canvas canvas) {
-        long nextUpdate = nDraw(mNativePtr, canvas.getNativeCanvasWrapper());
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called draw on empty AnimatedImageDrawable");
+        }
+
+        if (mStarting) {
+            mStarting = false;
+
+            postOnAnimationStart();
+        }
+
+        long nextUpdate = nDraw(mState.mNativePtr, canvas.getNativeCanvasWrapper());
         // a value <= 0 indicates that the drawable is stopped or that renderThread
         // will manage the animation
         if (nextUpdate > 0) {
+            if (mRunnable == null) {
+                mRunnable = this::invalidateSelf;
+            }
             scheduleSelf(mRunnable, nextUpdate);
+        } else if (nextUpdate == FINISHED) {
+            // This means the animation was drawn in software mode and ended.
+            postOnAnimationEnd();
         }
     }
 
     @Override
-    public void setAlpha(@IntRange(from=0,to=255) int alpha) {
+    public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
         if (alpha < 0 || alpha > 255) {
             throw new IllegalArgumentException("Alpha must be between 0 and"
                    + " 255! provided " + alpha);
         }
-        nSetAlpha(mNativePtr, alpha);
+
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called setAlpha on empty AnimatedImageDrawable");
+        }
+
+        nSetAlpha(mState.mNativePtr, alpha);
         invalidateSelf();
     }
 
     @Override
     public int getAlpha() {
-        return nGetAlpha(mNativePtr);
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called getAlpha on empty AnimatedImageDrawable");
+        }
+        return nGetAlpha(mState.mNativePtr);
     }
 
     @Override
     public void setColorFilter(@Nullable ColorFilter colorFilter) {
-        long nativeFilter = colorFilter == null ? 0 : colorFilter.getNativeInstance();
-        nSetColorFilter(mNativePtr, nativeFilter);
-        invalidateSelf();
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called setColorFilter on empty AnimatedImageDrawable");
+        }
+
+        if (colorFilter != mColorFilter) {
+            mColorFilter = colorFilter;
+            long nativeFilter = colorFilter == null ? 0 : colorFilter.getNativeInstance();
+            nSetColorFilter(mState.mNativePtr, nativeFilter);
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    @Nullable
+    public ColorFilter getColorFilter() {
+        return mColorFilter;
     }
 
     @Override
@@ -153,39 +403,220 @@
         return PixelFormat.TRANSLUCENT;
     }
 
-    // TODO: Add a Constant State?
-    // @Override
-    // public @Nullable ConstantState getConstantState() {}
-
-
-    // Animatable overrides
     @Override
-    public boolean isRunning() {
-        return nIsRunning(mNativePtr);
-    }
-
-    @Override
-    public void start() {
-        if (nStart(mNativePtr)) {
-            invalidateSelf();
+    public void setAutoMirrored(boolean mirrored) {
+        if (mState.mAutoMirrored != mirrored) {
+            mState.mAutoMirrored = mirrored;
+            if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL && mState.mNativePtr != 0) {
+                nSetMirrored(mState.mNativePtr, mirrored);
+                invalidateSelf();
+            }
         }
     }
 
     @Override
-    public void stop() {
-        nStop(mNativePtr);
+    public boolean onLayoutDirectionChanged(int layoutDirection) {
+        if (!mState.mAutoMirrored || mState.mNativePtr == 0) {
+            return false;
+        }
+
+        final boolean mirror = layoutDirection == View.LAYOUT_DIRECTION_RTL;
+        nSetMirrored(mState.mNativePtr, mirror);
+        return true;
     }
 
+    @Override
+    public final boolean isAutoMirrored() {
+        return mState.mAutoMirrored;
+    }
+
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        if (!super.setVisible(visible, restart)) {
+            return false;
+        }
+
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called setVisible on empty AnimatedImageDrawable");
+        }
+
+        if (!visible) {
+            nMarkInvisible(mState.mNativePtr);
+        }
+
+        return true;
+    }
+
+    // Animatable overrides
+    /**
+     *  Return whether the animation is currently running.
+     *
+     *  <p>When this drawable is created, this will return {@code false}. A client
+     *  needs to call {@link #start} to start the animation.</p>
+     */
+    @Override
+    public boolean isRunning() {
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called isRunning on empty AnimatedImageDrawable");
+        }
+        return nIsRunning(mState.mNativePtr);
+    }
+
+    /**
+     *  Start the animation.
+     *
+     *  <p>Does nothing if the animation is already running. If the animation is stopped,
+     *  this will reset it.</p>
+     *
+     *  <p>When the drawable is drawn, starting the animation,
+     *  {@link Animatable2.AnimationCallback#onAnimationStart} will be called.</p>
+     */
+    @Override
+    public void start() {
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called start on empty AnimatedImageDrawable");
+        }
+
+        if (nStart(mState.mNativePtr)) {
+            mStarting = true;
+            invalidateSelf();
+        }
+    }
+
+    /**
+     *  Stop the animation.
+     *
+     *  <p>If the animation is stopped, it will continue to display the frame
+     *  it was displaying when stopped.</p>
+     */
+    @Override
+    public void stop() {
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called stop on empty AnimatedImageDrawable");
+        }
+        if (nStop(mState.mNativePtr)) {
+            postOnAnimationEnd();
+        }
+    }
+
+    // Animatable2 overrides
+    private ArrayList<Animatable2.AnimationCallback> mAnimationCallbacks = null;
+
+    @Override
+    public void registerAnimationCallback(@NonNull AnimationCallback callback) {
+        if (callback == null) {
+            return;
+        }
+
+        if (mAnimationCallbacks == null) {
+            mAnimationCallbacks = new ArrayList<Animatable2.AnimationCallback>();
+            nSetOnAnimationEndListener(mState.mNativePtr, this);
+        }
+
+        if (!mAnimationCallbacks.contains(callback)) {
+            mAnimationCallbacks.add(callback);
+        }
+    }
+
+    @Override
+    public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) {
+        if (callback == null || mAnimationCallbacks == null
+                || !mAnimationCallbacks.remove(callback)) {
+            return false;
+        }
+
+        if (mAnimationCallbacks.isEmpty()) {
+            clearAnimationCallbacks();
+        }
+
+        return true;
+    }
+
+    @Override
+    public void clearAnimationCallbacks() {
+        if (mAnimationCallbacks != null) {
+            mAnimationCallbacks = null;
+            nSetOnAnimationEndListener(mState.mNativePtr, null);
+        }
+    }
+
+    private void postOnAnimationStart() {
+        if (mAnimationCallbacks == null) {
+            return;
+        }
+
+        getHandler().post(() -> {
+            for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
+                callback.onAnimationStart(this);
+            }
+        });
+    }
+
+    private void postOnAnimationEnd() {
+        if (mAnimationCallbacks == null) {
+            return;
+        }
+
+        getHandler().post(() -> {
+            for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
+                callback.onAnimationEnd(this);
+            }
+        });
+    }
+
+    private Handler getHandler() {
+        if (mHandler == null) {
+            mHandler = new Handler(Looper.getMainLooper());
+        }
+        return mHandler;
+    }
+
+    /**
+     *  Called by JNI.
+     *
+     *  The JNI code has already posted this to the thread that created the
+     *  callback, so no need to post.
+     */
+    @SuppressWarnings("unused")
+    private void onAnimationEnd() {
+        if (mAnimationCallbacks != null) {
+            for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
+                callback.onAnimationEnd(this);
+            }
+        }
+    }
+
+
     private static native long nCreate(long nativeImageDecoder,
             @Nullable ImageDecoder decoder, int width, int height, Rect cropRect)
         throws IOException;
+    @FastNative
     private static native long nGetNativeFinalizer();
     private static native long nDraw(long nativePtr, long canvasNativePtr);
+    @FastNative
     private static native void nSetAlpha(long nativePtr, int alpha);
+    @FastNative
     private static native int nGetAlpha(long nativePtr);
+    @FastNative
     private static native void nSetColorFilter(long nativePtr, long nativeFilter);
+    @FastNative
     private static native boolean nIsRunning(long nativePtr);
+    // Return whether the animation started.
+    @FastNative
     private static native boolean nStart(long nativePtr);
-    private static native void nStop(long nativePtr);
+    @FastNative
+    private static native boolean nStop(long nativePtr);
+    @FastNative
+    private static native int nGetRepeatCount(long nativePtr);
+    @FastNative
+    private static native void nSetRepeatCount(long nativePtr, int repeatCount);
+    // Pass the drawable down to native so it can call onAnimationEnd.
+    private static native void nSetOnAnimationEndListener(long nativePtr,
+            @Nullable AnimatedImageDrawable drawable);
+    @FastNative
     private static native long nNativeByteSize(long nativePtr);
+    @FastNative
+    private static native void nMarkInvisible(long nativePtr);
+    @FastNative
+    private static native void nSetMirrored(long nativePtr, boolean mirror);
 }
diff --git a/android/graphics/drawable/AnimatedVectorDrawable.java b/android/graphics/drawable/AnimatedVectorDrawable.java
index e74dc6d..54358e3 100644
--- a/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -201,10 +201,10 @@
  *     android:drawable=&quot;@drawable/vectordrawable&quot; &gt;
  *     &lt;target
  *         android:name=&quot;rotationGroup&quot;
- *         android:animation=&quot;@anim/rotation&quot; /&gt;
+ *         android:animation=&quot;@animator/rotation&quot; /&gt;
  *     &lt;target
  *         android:name=&quot;v&quot;
- *         android:animation=&quot;@anim/path_morph&quot; /&gt;
+ *         android:animation=&quot;@animator/path_morph&quot; /&gt;
  * &lt;/animated-vector&gt;
  * </pre>
  * </li>
diff --git a/android/graphics/drawable/BitmapDrawable.java b/android/graphics/drawable/BitmapDrawable.java
index 7ad062a..44b783b 100644
--- a/android/graphics/drawable/BitmapDrawable.java
+++ b/android/graphics/drawable/BitmapDrawable.java
@@ -27,6 +27,7 @@
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Outline;
@@ -49,6 +50,7 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -111,7 +113,7 @@
      */
     @Deprecated
     public BitmapDrawable() {
-        mBitmapState = new BitmapState((Bitmap) null);
+        init(new BitmapState((Bitmap) null), null);
     }
 
     /**
@@ -124,8 +126,7 @@
     @SuppressWarnings("unused")
     @Deprecated
     public BitmapDrawable(Resources res) {
-        mBitmapState = new BitmapState((Bitmap) null);
-        mBitmapState.mTargetDensity = mTargetDensity;
+        init(new BitmapState((Bitmap) null), res);
     }
 
     /**
@@ -135,7 +136,7 @@
      */
     @Deprecated
     public BitmapDrawable(Bitmap bitmap) {
-        this(new BitmapState(bitmap), null);
+        init(new BitmapState(bitmap), null);
     }
 
     /**
@@ -143,8 +144,7 @@
      * the display metrics of the resources.
      */
     public BitmapDrawable(Resources res, Bitmap bitmap) {
-        this(new BitmapState(bitmap), res);
-        mBitmapState.mTargetDensity = mTargetDensity;
+        init(new BitmapState(bitmap), res);
     }
 
     /**
@@ -154,10 +154,7 @@
      */
     @Deprecated
     public BitmapDrawable(String filepath) {
-        this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
-        }
+        this(null, filepath);
     }
 
     /**
@@ -165,10 +162,21 @@
      */
     @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
     public BitmapDrawable(Resources res, String filepath) {
-        this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
-        mBitmapState.mTargetDensity = mTargetDensity;
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+        Bitmap bitmap = null;
+        try (FileInputStream stream = new FileInputStream(filepath)) {
+            bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream),
+                    (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+            });
+        } catch (Exception e) {
+            /*  do nothing. This matches the behavior of BitmapFactory.decodeFile()
+                If the exception happened on decode, mBitmapState.mBitmap will be null.
+            */
+        } finally {
+            init(new BitmapState(bitmap), res);
+            if (mBitmapState.mBitmap == null) {
+                android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+            }
         }
     }
 
@@ -179,10 +187,7 @@
      */
     @Deprecated
     public BitmapDrawable(java.io.InputStream is) {
-        this(new BitmapState(BitmapFactory.decodeStream(is)), null);
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
-        }
+        this(null, is);
     }
 
     /**
@@ -190,10 +195,21 @@
      */
     @SuppressWarnings({ "unused", "ChainingConstructorIgnoresParameter" })
     public BitmapDrawable(Resources res, java.io.InputStream is) {
-        this(new BitmapState(BitmapFactory.decodeStream(is)), null);
-        mBitmapState.mTargetDensity = mTargetDensity;
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+        Bitmap bitmap = null;
+        try {
+            bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is),
+                    (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+            });
+        } catch (Exception e) {
+            /*  do nothing. This matches the behavior of BitmapFactory.decodeStream()
+                If the exception happened on decode, mBitmapState.mBitmap will be null.
+            */
+        } finally {
+            init(new BitmapState(bitmap), res);
+            if (mBitmapState.mBitmap == null) {
+                android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+            }
         }
     }
 
@@ -812,9 +828,19 @@
                 }
             }
 
+            int density = Bitmap.DENSITY_NONE;
+            if (value.density == TypedValue.DENSITY_DEFAULT) {
+                density = DisplayMetrics.DENSITY_DEFAULT;
+            } else if (value.density != TypedValue.DENSITY_NONE) {
+                density = value.density;
+            }
+
             Bitmap bitmap = null;
             try (InputStream is = r.openRawResource(srcResId, value)) {
-                bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null);
+                ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+                bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+                });
             } catch (Exception e) {
                 // Do nothing and pick up the error below.
             }
@@ -1013,14 +1039,21 @@
         }
     }
 
+    private BitmapDrawable(BitmapState state, Resources res) {
+        init(state, res);
+    }
+
     /**
-     * The one constructor to rule them all. This is called by all public
+     * The one helper to rule them all. This is called by all public & private
      * constructors to set the state and initialize local properties.
      */
-    private BitmapDrawable(BitmapState state, Resources res) {
+    private void init(BitmapState state, Resources res) {
         mBitmapState = state;
-
         updateLocalState(res);
+
+        if (mBitmapState != null && res != null) {
+            mBitmapState.mTargetDensity = mTargetDensity;
+        }
     }
 
     /**
diff --git a/android/graphics/drawable/Drawable.java b/android/graphics/drawable/Drawable.java
index f17cd76..8af2fd8 100644
--- a/android/graphics/drawable/Drawable.java
+++ b/android/graphics/drawable/Drawable.java
@@ -37,6 +37,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
 import android.graphics.Insets;
 import android.graphics.NinePatch;
 import android.graphics.Outline;
@@ -50,11 +51,13 @@
 import android.os.Trace;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.StateSet;
 import android.util.TypedValue;
 import android.util.Xml;
 import android.view.View;
 
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
@@ -1168,13 +1171,21 @@
     /**
      * Create a drawable from an inputstream, using the given resources and
      * value to determine density information.
+     *
+     * @deprecated Prefer the version without an Options object.
      */
-    public static Drawable createFromResourceStream(Resources res, TypedValue value,
-            InputStream is, String srcName, BitmapFactory.Options opts) {
+    @Nullable
+    public static Drawable createFromResourceStream(@Nullable Resources res,
+            @Nullable TypedValue value, @Nullable InputStream is, @Nullable String srcName,
+            @Nullable BitmapFactory.Options opts) {
         if (is == null) {
             return null;
         }
 
+        if (opts == null) {
+            return getBitmapDrawable(res, value, is);
+        }
+
         /*  ugh. The decodeStream contract is that we have already allocated
             the pad rect, but if the bitmap does not had a ninepatch chunk,
             then the pad will be ignored. If we could change this to lazily
@@ -1190,7 +1201,6 @@
         // an application in compatibility mode, without scaling those down
         // to the compatibility density only to have them scaled back up when
         // drawn to the screen.
-        if (opts == null) opts = new BitmapFactory.Options();
         opts.inScreenDensity = Drawable.resolveDensity(res, 0);
         Bitmap  bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
         if (bm != null) {
@@ -1207,6 +1217,33 @@
         return null;
     }
 
+    private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) {
+        try {
+            ImageDecoder.Source source = null;
+            if (value != null) {
+                int density = Bitmap.DENSITY_NONE;
+                if (value.density == TypedValue.DENSITY_DEFAULT) {
+                    density = DisplayMetrics.DENSITY_DEFAULT;
+                } else if (value.density != TypedValue.DENSITY_NONE) {
+                    density = value.density;
+                }
+                source = ImageDecoder.createSource(res, is, density);
+            } else {
+                source = ImageDecoder.createSource(res, is);
+            }
+
+            return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+            });
+        } catch (IOException e) {
+            /*  do nothing.
+                If the exception happened on decode, the drawable will be null.
+            */
+            Log.e("Drawable", "Unable to decode stream: " + e);
+        }
+        return null;
+    }
+
     /**
      * Create a drawable from an XML document. For more information on how to
      * create resources in XML, see
@@ -1306,11 +1343,10 @@
         }
 
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);
-        try {
-            Bitmap bm = BitmapFactory.decodeFile(pathName);
-            if (bm != null) {
-                return drawableFromBitmap(null, bm, null, null, null, pathName);
-            }
+        try (FileInputStream stream = new FileInputStream(pathName)) {
+            return getBitmapDrawable(null, null, stream);
+        } catch(IOException e) {
+            // Do nothing; we will just return null if the FileInputStream had an error
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
diff --git a/android/graphics/drawable/DrawableInflater.java b/android/graphics/drawable/DrawableInflater.java
index eea7048..0ee9071 100644
--- a/android/graphics/drawable/DrawableInflater.java
+++ b/android/graphics/drawable/DrawableInflater.java
@@ -185,6 +185,8 @@
                 return new BitmapDrawable();
             case "nine-patch":
                 return new NinePatchDrawable();
+            case "animated-image":
+                return new AnimatedImageDrawable();
             default:
                 return null;
         }
diff --git a/android/graphics/drawable/GradientDrawable.java b/android/graphics/drawable/GradientDrawable.java
index 6c3aea2..dfdddb2 100644
--- a/android/graphics/drawable/GradientDrawable.java
+++ b/android/graphics/drawable/GradientDrawable.java
@@ -42,6 +42,7 @@
 import android.graphics.RectF;
 import android.graphics.Shader;
 import android.graphics.SweepGradient;
+import android.graphics.Xfermode;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -814,6 +815,24 @@
         }
     }
 
+    /**
+     * @param mode to draw this drawable with
+     * @hide
+     */
+    @Override
+    public void setXfermode(@Nullable Xfermode mode) {
+        super.setXfermode(mode);
+        mFillPaint.setXfermode(mode);
+    }
+
+    /**
+     * @param aa to draw this drawable with
+     * @hide
+     */
+    public void setAntiAlias(boolean aa) {
+        mFillPaint.setAntiAlias(aa);
+    }
+
     private void buildPathIfDirty() {
         final GradientState st = mGradientState;
         if (mPathIsDirty) {
@@ -2074,6 +2093,7 @@
             }
             mRadius = radius;
             mRadiusArray = null;
+            computeOpacity();
         }
 
         public void setCornerRadii(float[] radii) {
@@ -2081,6 +2101,7 @@
             if (radii == null) {
                 mRadius = 0;
             }
+            computeOpacity();
         }
 
         public void setSize(int width, int height) {
diff --git a/android/graphics/drawable/Icon.java b/android/graphics/drawable/Icon.java
index 749b759..361fe0b 100644
--- a/android/graphics/drawable/Icon.java
+++ b/android/graphics/drawable/Icon.java
@@ -18,11 +18,14 @@
 
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
-import android.content.res.ColorStateList;
+import android.annotation.IdRes;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -60,17 +63,40 @@
 public final class Icon implements Parcelable {
     private static final String TAG = "Icon";
 
-    /** @hide */
+    /**
+     * An icon that was created using {@link Icon#createWithBitmap(Bitmap)}.
+     * @see #getType
+     */
     public static final int TYPE_BITMAP   = 1;
-    /** @hide */
+    /**
+     * An icon that was created using {@link Icon#createWithResource}.
+     * @see #getType
+     */
     public static final int TYPE_RESOURCE = 2;
-    /** @hide */
+    /**
+     * An icon that was created using {@link Icon#createWithData(byte[], int, int)}.
+     * @see #getType
+     */
     public static final int TYPE_DATA     = 3;
-    /** @hide */
+    /**
+     * An icon that was created using {@link Icon#createWithContentUri}
+     * or {@link Icon#createWithFilePath(String)}.
+     * @see #getType
+     */
     public static final int TYPE_URI      = 4;
-    /** @hide */
+    /**
+     * An icon that was created using {@link Icon#createWithAdaptiveBitmap}.
+     * @see #getType
+     */
     public static final int TYPE_ADAPTIVE_BITMAP = 5;
 
+    /**
+     * @hide
+     */
+    @IntDef({TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP})
+    public @interface IconType {
+    }
+
     private static final int VERSION_STREAM_SERIALIZER = 1;
 
     private final int mType;
@@ -99,14 +125,12 @@
     private int             mInt2;
 
     /**
-     * @return The type of image data held in this Icon. One of
-     * {@link #TYPE_BITMAP},
-     * {@link #TYPE_RESOURCE},
-     * {@link #TYPE_DATA}, or
-     * {@link #TYPE_URI}.
-     * {@link #TYPE_ADAPTIVE_BITMAP}
-     * @hide
+     * Gets the type of the icon provided.
+     * <p>
+     * Note that new types may be added later, so callers should guard against other
+     * types being returned.
      */
+    @IconType
     public int getType() {
         return mType;
     }
@@ -179,9 +203,13 @@
     }
 
     /**
-     * @return The package containing resources for this {@link #TYPE_RESOURCE} Icon.
-     * @hide
+     * Gets the package used to create this icon.
+     * <p>
+     * Only valid for icons of type {@link #TYPE_RESOURCE}.
+     * Note: This package may not be available if referenced in the future, and it is
+     * up to the caller to ensure safety if this package is re-used and/or persisted.
      */
+    @NonNull
     public String getResPackage() {
         if (mType != TYPE_RESOURCE) {
             throw new IllegalStateException("called getResPackage() on " + this);
@@ -190,9 +218,13 @@
     }
 
     /**
-     * @return The resource ID for this {@link #TYPE_RESOURCE} Icon.
-     * @hide
+     * Gets the resource used to create this icon.
+     * <p>
+     * Only valid for icons of type {@link #TYPE_RESOURCE}.
+     * Note: This resource may not be available if the application changes at all, and it is
+     * up to the caller to ensure safety if this resource is re-used and/or persisted.
      */
+    @IdRes
     public int getResId() {
         if (mType != TYPE_RESOURCE) {
             throw new IllegalStateException("called getResId() on " + this);
@@ -212,9 +244,13 @@
     }
 
     /**
-     * @return The {@link android.net.Uri} for this {@link #TYPE_URI} Icon.
-     * @hide
+     * Gets the uri used to create this icon.
+     * <p>
+     * Only valid for icons of type {@link #TYPE_URI}.
+     * Note: This uri may not be available in the future, and it is
+     * up to the caller to ensure safety if this uri is re-used and/or persisted.
      */
+    @NonNull
     public Uri getUri() {
         return Uri.parse(getUriString());
     }
diff --git a/android/graphics/drawable/NinePatchDrawable.java b/android/graphics/drawable/NinePatchDrawable.java
index 1790020..66f2a31 100644
--- a/android/graphics/drawable/NinePatchDrawable.java
+++ b/android/graphics/drawable/NinePatchDrawable.java
@@ -24,9 +24,9 @@
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
 import android.graphics.Insets;
 import android.graphics.NinePatch;
 import android.graphics.Outline;
@@ -211,7 +211,8 @@
             restoreAlpha = -1;
         }
 
-        final boolean needsDensityScaling = canvas.getDensity() == 0;
+        final boolean needsDensityScaling = canvas.getDensity() == 0
+                && Bitmap.DENSITY_NONE != state.mNinePatch.getDensity();
         if (needsDensityScaling) {
             restoreToCount = restoreToCount >= 0 ? restoreToCount : canvas.save();
 
@@ -421,10 +422,6 @@
 
         final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0);
         if (srcResId != 0) {
-            final BitmapFactory.Options options = new BitmapFactory.Options();
-            options.inDither = !state.mDither;
-            options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi;
-
             final Rect padding = new Rect();
             final Rect opticalInsets = new Rect();
             Bitmap bitmap = null;
@@ -433,7 +430,17 @@
                 final TypedValue value = new TypedValue();
                 final InputStream is = r.openRawResource(srcResId, value);
 
-                bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options);
+                int density = Bitmap.DENSITY_NONE;
+                if (value.density == TypedValue.DENSITY_DEFAULT) {
+                    density = DisplayMetrics.DENSITY_DEFAULT;
+                } else if (value.density != TypedValue.DENSITY_NONE) {
+                    density = value.density;
+                }
+                ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+                bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+                    decoder.setOutPaddingRect(padding);
+                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+                });
 
                 is.close();
             } catch (IOException e) {
@@ -660,8 +667,9 @@
             return;
         }
 
-        final int sourceDensity = ninePatch.getDensity();
         final int targetDensity = mTargetDensity;
+        final int sourceDensity = ninePatch.getDensity() == Bitmap.DENSITY_NONE ?
+            targetDensity : ninePatch.getDensity();
 
         final Insets sourceOpticalInsets = mNinePatchState.mOpticalInsets;
         if (sourceOpticalInsets != Insets.NONE) {
diff --git a/android/graphics/drawable/RippleBackground.java b/android/graphics/drawable/RippleBackground.java
index 41d3698..2812abe 100644
--- a/android/graphics/drawable/RippleBackground.java
+++ b/android/graphics/drawable/RippleBackground.java
@@ -78,9 +78,10 @@
     }
 
     private void onStateChanged() {
-        float newOpacity = 0.0f;
-        if (mHovered) newOpacity += .25f;
-        if (mFocused) newOpacity += .75f;
+        // Hover             = .2 * alpha
+        // Focus             = .6 * alpha
+        // Focused + Hovered = .6 * alpha
+        float newOpacity = mFocused ? .6f : mHovered ? .2f : 0f;
         if (mAnimator != null) {
             mAnimator.cancel();
             mAnimator = null;
diff --git a/android/graphics/drawable/RippleForeground.java b/android/graphics/drawable/RippleForeground.java
index 4129868..a8dc34a 100644
--- a/android/graphics/drawable/RippleForeground.java
+++ b/android/graphics/drawable/RippleForeground.java
@@ -110,6 +110,7 @@
 
         // Take 60% of the maximum of the width and height, then divided half to get the radius.
         mStartRadius = Math.max(bounds.width(), bounds.height()) * 0.3f;
+        clampStartingPosition();
     }
 
     @Override
@@ -350,7 +351,7 @@
         final float cY = mBounds.exactCenterY();
         final float dX = mStartingX - cX;
         final float dY = mStartingY - cY;
-        final float r = mTargetRadius;
+        final float r = mTargetRadius - mStartRadius;
         if (dX * dX + dY * dY > r * r) {
             // Point is outside the circle, clamp to the perimeter.
             final double angle = Math.atan2(dY, dX);