Merge "Prefer animation in ImageDecoder.decodeDrawable"
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 98162af..ec91cbf 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -78,14 +78,18 @@
     return nullptr;
 }
 
-static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream, jobject source) {
+static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream,
+        jobject source, jboolean preferAnimation) {
     if (!stream.get()) {
         return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to create a stream",
                                nullptr, source);
     }
     std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
     SkCodec::Result result;
-    auto codec = SkCodec::MakeFromStream(std::move(stream), &result, decoder->mPeeker.get());
+    auto codec = SkCodec::MakeFromStream(
+            std::move(stream), &result, decoder->mPeeker.get(),
+            preferAnimation ? SkCodec::SelectionPolicy::kPreferAnimation
+                            : SkCodec::SelectionPolicy::kPreferStillImage);
     if (jthrowable jexception = get_and_clear_exception(env)) {
         return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
     }
@@ -124,7 +128,7 @@
 }
 
 static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
-        jobject fileDescriptor, jobject source) {
+        jobject fileDescriptor, jboolean preferAnimation, jobject source) {
     int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
 
     struct stat fdStat;
@@ -142,11 +146,11 @@
     }
 
     std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file));
-    return native_create(env, std::move(fileStream), source);
+    return native_create(env, std::move(fileStream), source, preferAnimation);
 }
 
 static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/,
-        jobject is, jbyteArray storage, jobject source) {
+        jobject is, jbyteArray storage, jboolean preferAnimation, jobject source) {
     std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false));
 
     if (!stream.get()) {
@@ -157,31 +161,33 @@
     std::unique_ptr<SkStream> bufferedStream(
         SkFrontBufferedStream::Make(std::move(stream),
         SkCodec::MinBufferedBytesNeeded()));
-    return native_create(env, std::move(bufferedStream), source);
+    return native_create(env, std::move(bufferedStream), source, preferAnimation);
 }
 
-static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, jlong assetPtr,
-                                         jobject source) {
+static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/,
+        jlong assetPtr, jboolean preferAnimation, jobject source) {
     Asset* asset = reinterpret_cast<Asset*>(assetPtr);
     std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset));
-    return native_create(env, std::move(stream), source);
+    return native_create(env, std::move(stream), source, preferAnimation);
 }
 
-static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/, jobject jbyteBuffer,
-                                              jint initialPosition, jint limit, jobject source) {
+static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/,
+        jobject jbyteBuffer, jint initialPosition, jint limit,
+        jboolean preferAnimation, jobject source) {
     std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer,
                                                                      initialPosition, limit);
     if (!stream) {
         return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to read ByteBuffer",
                                nullptr, source);
     }
-    return native_create(env, std::move(stream), source);
+    return native_create(env, std::move(stream), source, preferAnimation);
 }
 
-static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, jbyteArray byteArray,
-                                             jint offset, jint length, jobject source) {
+static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/,
+        jbyteArray byteArray, jint offset, jint length,
+        jboolean preferAnimation, jobject source) {
     std::unique_ptr<SkStream> stream(CreateByteArrayStreamAdaptor(env, byteArray, offset, length));
-    return native_create(env, std::move(stream), source);
+    return native_create(env, std::move(stream), source, preferAnimation);
 }
 
 jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) {
@@ -514,11 +520,11 @@
 }
 
 static const JNINativeMethod gImageDecoderMethods[] = {
-    { "nCreate",        "(JLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;",    (void*) ImageDecoder_nCreateAsset },
-    { "nCreate",        "(Ljava/nio/ByteBuffer;IILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
-    { "nCreate",        "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
-    { "nCreate",        "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
-    { "nCreate",        "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
+    { "nCreate",        "(JZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;",    (void*) ImageDecoder_nCreateAsset },
+    { "nCreate",        "(Ljava/nio/ByteBuffer;IIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
+    { "nCreate",        "([BIIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
+    { "nCreate",        "(Ljava/io/InputStream;[BZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
+    { "nCreate",        "(Ljava/io/FileDescriptor;ZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
     { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZJZ)Landroid/graphics/Bitmap;",
                                                                  (void*) ImageDecoder_nDecodeBitmap },
     { "nGetSampledSize","(JI)Landroid/util/Size;",               (void*) ImageDecoder_nGetSampledSize },
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 2d5babc..150a941 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -214,7 +214,7 @@
 
         /* @hide */
         @NonNull
-        abstract ImageDecoder createImageDecoder() throws IOException;
+        abstract ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException;
     };
 
     private static class ByteArraySource extends Source {
@@ -228,8 +228,8 @@
         private final int    mLength;
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
-            return nCreate(mData, mOffset, mLength, this);
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
+            return nCreate(mData, mOffset, mLength, preferAnimation, this);
         }
     }
 
@@ -240,14 +240,14 @@
         private final ByteBuffer mBuffer;
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) 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, this);
+                return nCreate(mBuffer.array(), offset, length, preferAnimation, this);
             }
             ByteBuffer buffer = mBuffer.slice();
-            return nCreate(buffer, buffer.position(), buffer.limit(), this);
+            return nCreate(buffer, buffer.position(), buffer.limit(), preferAnimation, this);
         }
     }
 
@@ -267,7 +267,7 @@
         Resources getResources() { return mResources; }
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
             AssetFileDescriptor assetFd = null;
             try {
                 if (mUri.getScheme() == ContentResolver.SCHEME_CONTENT) {
@@ -284,26 +284,26 @@
                     throw new FileNotFoundException(mUri.toString());
                 }
 
-                return createFromStream(is, true, this);
+                return createFromStream(is, true, preferAnimation, this);
             }
-            return createFromAssetFileDescriptor(assetFd, this);
+            return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
         }
     }
 
     @NonNull
     private static ImageDecoder createFromFile(@NonNull File file,
-            @NonNull Source source) throws IOException {
+            boolean preferAnimation, @NonNull Source source) throws IOException {
         FileInputStream stream = new FileInputStream(file);
         FileDescriptor fd = stream.getFD();
         try {
             Os.lseek(fd, 0, SEEK_CUR);
         } catch (ErrnoException e) {
-            return createFromStream(stream, true, source);
+            return createFromStream(stream, true, preferAnimation, source);
         }
 
         ImageDecoder decoder = null;
         try {
-            decoder = nCreate(fd, source);
+            decoder = nCreate(fd, preferAnimation, source);
         } finally {
             if (decoder == null) {
                 IoUtils.closeQuietly(stream);
@@ -317,12 +317,12 @@
 
     @NonNull
     private static ImageDecoder createFromStream(@NonNull InputStream is,
-            boolean closeInputStream, Source source) throws IOException {
+            boolean closeInputStream, boolean preferAnimation, Source source) throws IOException {
         // Arbitrary size matches BitmapFactory.
         byte[] storage = new byte[16 * 1024];
         ImageDecoder decoder = null;
         try {
-            decoder = nCreate(is, storage, source);
+            decoder = nCreate(is, storage, preferAnimation, source);
         } finally {
             if (decoder == null) {
                 if (closeInputStream) {
@@ -340,7 +340,7 @@
 
     @NonNull
     private static ImageDecoder createFromAssetFileDescriptor(@NonNull AssetFileDescriptor assetFd,
-            Source source) throws IOException {
+            boolean preferAnimation, Source source) throws IOException {
         final FileDescriptor fd = assetFd.getFileDescriptor();
         final long offset = assetFd.getStartOffset();
 
@@ -348,9 +348,9 @@
         try {
             try {
                 Os.lseek(fd, offset, SEEK_SET);
-                decoder = nCreate(fd, source);
+                decoder = nCreate(fd, preferAnimation, source);
             } catch (ErrnoException e) {
-                decoder = createFromStream(new FileInputStream(fd), true, source);
+                decoder = createFromStream(new FileInputStream(fd), true, preferAnimation, source);
             }
         } finally {
             if (decoder == null) {
@@ -388,7 +388,7 @@
         public int getDensity() { return mInputDensity; }
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
 
             synchronized (this) {
                 if (mInputStream == null) {
@@ -396,7 +396,7 @@
                 }
                 InputStream is = mInputStream;
                 mInputStream = null;
-                return createFromStream(is, false, this);
+                return createFromStream(is, false, preferAnimation, this);
             }
         }
     }
@@ -434,14 +434,14 @@
         }
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
             synchronized (this) {
                 if (mAssetInputStream == null) {
                     throw new IOException("Cannot reuse AssetInputStreamSource");
                 }
                 AssetInputStream ais = mAssetInputStream;
                 mAssetInputStream = null;
-                return createFromAsset(ais, this);
+                return createFromAsset(ais, preferAnimation, this);
             }
         }
     }
@@ -469,7 +469,7 @@
         }
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
             TypedValue value = new TypedValue();
             // This is just used in order to access the underlying Asset and
             // keep it alive.
@@ -483,7 +483,7 @@
                 }
             }
 
-            return createFromAsset((AssetInputStream) is, this);
+            return createFromAsset((AssetInputStream) is, preferAnimation, this);
         }
     }
 
@@ -491,11 +491,11 @@
      *  ImageDecoder will own the AssetInputStream.
      */
     private static ImageDecoder createFromAsset(AssetInputStream ais,
-            Source source) throws IOException {
+            boolean preferAnimation, Source source) throws IOException {
         ImageDecoder decoder = null;
         try {
             long asset = ais.getNativeAsset();
-            decoder = nCreate(asset, source);
+            decoder = nCreate(asset, preferAnimation, source);
         } finally {
             if (decoder == null) {
                 IoUtils.closeQuietly(ais);
@@ -517,9 +517,9 @@
         private final String mFileName;
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
             InputStream is = mAssets.open(mFileName);
-            return createFromAsset((AssetInputStream) is, this);
+            return createFromAsset((AssetInputStream) is, preferAnimation, this);
         }
     }
 
@@ -531,8 +531,8 @@
         private final File mFile;
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
-            return createFromFile(mFile, this);
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
+            return createFromFile(mFile, preferAnimation, this);
         }
     }
 
@@ -544,7 +544,7 @@
         private final Callable<AssetFileDescriptor> mCallable;
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
             AssetFileDescriptor assetFd = null;
             try {
                 assetFd = mCallable.call();
@@ -555,7 +555,7 @@
                     throw new IOException(e);
                 }
             }
-            return createFromAssetFileDescriptor(assetFd, this);
+            return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
         }
     }
 
@@ -1740,7 +1740,7 @@
     @NonNull
     private static Drawable decodeDrawableImpl(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
-        try (ImageDecoder decoder = src.createImageDecoder()) {
+        try (ImageDecoder decoder = src.createImageDecoder(true /*preferAnimation*/)) {
             decoder.mSource = src;
             decoder.callHeaderDecoded(listener, src);
 
@@ -1844,7 +1844,7 @@
     @NonNull
     private static Bitmap decodeBitmapImpl(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
-        try (ImageDecoder decoder = src.createImageDecoder()) {
+        try (ImageDecoder decoder = src.createImageDecoder(false /*preferAnimation*/)) {
             decoder.mSource = src;
             decoder.callHeaderDecoded(listener, src);
 
@@ -1971,15 +1971,17 @@
         }
     }
 
-    private static native ImageDecoder nCreate(long asset, Source src) throws IOException;
-    private static native ImageDecoder nCreate(ByteBuffer buffer, int position,
-                                               int limit, Source src) throws IOException;
+    private static native ImageDecoder nCreate(long asset,
+            boolean preferAnimation, Source src) throws IOException;
+    private static native ImageDecoder nCreate(ByteBuffer buffer, int position, int limit,
+            boolean preferAnimation, Source src) throws IOException;
     private static native ImageDecoder nCreate(byte[] data, int offset, int length,
-                                               Source src) throws IOException;
+            boolean preferAnimation, Source src) throws IOException;
     private static native ImageDecoder nCreate(InputStream is, byte[] storage,
-                                               Source src) throws IOException;
+            boolean preferAnimation, Source src) throws IOException;
     // The fd must be seekable.
-    private static native ImageDecoder nCreate(FileDescriptor fd, Source src) throws IOException;
+    private static native ImageDecoder nCreate(FileDescriptor fd,
+            boolean preferAnimation, Source src) throws IOException;
     @NonNull
     private static native Bitmap nDecodeBitmap(long nativePtr,
             @NonNull ImageDecoder decoder,