Prohibit Config.HARDWARE in factory methods, that create mutable bitmaps

Test: android.cts.graphics.BitmapTest#testCreateMutableBitmapWithHardwareConfig
bug:30999911
Change-Id: I7cd4e2625563b6659613ccd180a57c56dcf7c2b1
diff --git a/api/current.txt b/api/current.txt
index 0209b5e..3af2c8f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11779,6 +11779,7 @@
     enum_constant public static final android.graphics.Bitmap.Config ALPHA_8;
     enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444;
     enum_constant public static final android.graphics.Bitmap.Config ARGB_8888;
+    enum_constant public static final android.graphics.Bitmap.Config HARDWARE;
     enum_constant public static final android.graphics.Bitmap.Config RGB_565;
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index 1d14343..8034c3b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -12265,6 +12265,7 @@
     enum_constant public static final android.graphics.Bitmap.Config ALPHA_8;
     enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444;
     enum_constant public static final android.graphics.Bitmap.Config ARGB_8888;
+    enum_constant public static final android.graphics.Bitmap.Config HARDWARE;
     enum_constant public static final android.graphics.Bitmap.Config RGB_565;
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 2ef7fa5..f10fdb0 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -11810,6 +11810,7 @@
     enum_constant public static final android.graphics.Bitmap.Config ALPHA_8;
     enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444;
     enum_constant public static final android.graphics.Bitmap.Config ARGB_8888;
+    enum_constant public static final android.graphics.Bitmap.Config HARDWARE;
     enum_constant public static final android.graphics.Bitmap.Config RGB_565;
   }
 
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index e86acc4..762a3f3 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -276,6 +276,11 @@
         }
     }
 
+    if (isMutable && isHardware) {
+        doThrowIAE(env, "Bitmaps with Config.HARWARE are always immutable");
+        return nullObjectReturn("Cannot create mutable hardware bitmap");
+    }
+
     // Create the codec.
     NinePatchPeeker peeker;
     std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(streamDeleter.release(),
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 756087c..cd75fe9 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -467,7 +467,11 @@
 
 
         /**
-          * @hide
+         * Special configuration, when bitmap is stored only in graphic memory.
+         * Bitmaps in this configuration are always immutable.
+         *
+         * It is optimal for cases, when the only operation with the bitmap is to draw it on a
+         * screen.
          */
         HARDWARE    (6);
 
@@ -810,7 +814,8 @@
      * @param width    The width of the bitmap
      * @param height   The height of the bitmap
      * @param config   The bitmap config to create.
-     * @throws IllegalArgumentException if the width or height are <= 0
+     * @throws IllegalArgumentException if the width or height are <= 0, or if
+     *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
      */
     public static Bitmap createBitmap(int width, int height, Config config) {
         return createBitmap(width, height, config, true);
@@ -825,7 +830,8 @@
      * @param width    The width of the bitmap
      * @param height   The height of the bitmap
      * @param config   The bitmap config to create.
-     * @throws IllegalArgumentException if the width or height are <= 0
+     * @throws IllegalArgumentException if the width or height are <= 0, or if
+     *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
      */
     public static Bitmap createBitmap(DisplayMetrics display, int width,
             int height, Config config) {
@@ -843,7 +849,8 @@
      *                 bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
      *
-     * @throws IllegalArgumentException if the width or height are <= 0
+     * @throws IllegalArgumentException if the width or height are <= 0, or if
+     *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
      */
     private static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) {
         return createBitmap(null, width, height, config, hasAlpha);
@@ -862,13 +869,17 @@
      *                 bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
      *
-     * @throws IllegalArgumentException if the width or height are <= 0
+     * @throws IllegalArgumentException if the width or height are <= 0, or if
+     *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
      */
     private static Bitmap createBitmap(DisplayMetrics display, int width, int height,
             Config config, boolean hasAlpha) {
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("width and height must be > 0");
         }
+        if (config == Config.HARDWARE) {
+            throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE");
+        }
         Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true);
         if (display != null) {
             bm.mDensity = display.densityDpi;
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 447a4c4..a5517f0 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import static android.graphics.BitmapFactory.Options.validate;
+
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.os.Trace;
@@ -103,6 +105,9 @@
          * If set, decode methods will always return a mutable Bitmap instead of
          * an immutable one. This can be used for instance to programmatically apply
          * effects to a Bitmap loaded through BitmapFactory.
+         * <p>Can not be set simultaneously with inPreferredConfig =
+         * {@link android.graphics.Bitmap.Config#HARDWARE},
+         * because hardware bitmaps are always immutable.
          */
         @SuppressWarnings({"UnusedDeclaration"}) // used in native code
         public boolean inMutable;
@@ -381,6 +386,12 @@
         public void requestCancelDecode() {
             mCancel = true;
         }
+
+        static void validate(Options opts) {
+            if (opts != null && opts.inMutable && opts.inPreferredConfig == Bitmap.Config.HARDWARE) {
+                throw new IllegalArgumentException("Bitmaps with Config.HARWARE are always immutable");
+            }
+        }
     }
 
     /**
@@ -393,8 +404,12 @@
      * @return The decoded bitmap, or null if the image data could not be
      *         decoded, or, if opts is non-null, if opts requested only the
      *         size be returned (in opts.outWidth and opts.outHeight)
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      */
     public static Bitmap decodeFile(String pathName, Options opts) {
+        validate(opts);
         Bitmap bm = null;
         InputStream stream = null;
         try {
@@ -431,10 +446,13 @@
     /**
      * Decode a new Bitmap from an InputStream. This InputStream was obtained from
      * resources, which we pass to be able to scale the bitmap accordingly.
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      */
     public static Bitmap decodeResourceStream(Resources res, TypedValue value,
             InputStream is, Rect pad, Options opts) {
-
+        validate(opts);
         if (opts == null) {
             opts = new Options();
         }
@@ -466,8 +484,12 @@
      * @return The decoded bitmap, or null if the image data could not be
      *         decoded, or, if opts is non-null, if opts requested only the
      *         size be returned (in opts.outWidth and opts.outHeight)
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      */
     public static Bitmap decodeResource(Resources res, int id, Options opts) {
+        validate(opts);
         Bitmap bm = null;
         InputStream is = null; 
         
@@ -520,11 +542,15 @@
      * @return The decoded bitmap, or null if the image data could not be
      *         decoded, or, if opts is non-null, if opts requested only the
      *         size be returned (in opts.outWidth and opts.outHeight)
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      */
     public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
         if ((offset | length) < 0 || data.length < offset + length) {
             throw new ArrayIndexOutOfBoundsException();
         }
+        validate(opts);
 
         Bitmap bm;
 
@@ -598,6 +624,9 @@
      * @return The decoded bitmap, or null if the image data could not be
      *         decoded, or, if opts is non-null, if opts requested only the
      *         size be returned (in opts.outWidth and opts.outHeight)
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      *
      * <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT},
      * if {@link InputStream#markSupported is.markSupported()} returns true,
@@ -610,6 +639,7 @@
         if (is == null) {
             return null;
         }
+        validate(opts);
 
         Bitmap bm = null;
 
@@ -673,8 +703,12 @@
      * @param opts null-ok; Options that control downsampling and whether the
      *             image should be completely decoded, or just its size returned.
      * @return the decoded bitmap, or null
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      */
     public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
+        validate(opts);
         Bitmap bm;
 
         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeFileDescriptor");
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index e689b08..04abca1 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -178,8 +178,12 @@
      *             inPurgeable is not supported.
      * @return The decoded bitmap, or null if the image data could not be
      *         decoded.
+     * @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
+     *         is {@link android.graphics.Bitmap.Config#HARDWARE}
+     *         and {@link BitmapFactory.Options#inMutable} is set.
      */
     public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
+        BitmapFactory.Options.validate(options);
         synchronized (mNativeLock) {
             checkRecycled("decodeRegion called on recycled region decoder");
             if (rect.right <= 0 || rect.bottom <= 0 || rect.left >= getWidth()