Add public API for @hidden Bitmap#createAshmemBitmap

Bug: 150395371
Test: I442cd0c7a849e4c6a2fe7ba21b25a6e44417bbf5

The system needs to move off of @hidden Bitmap APIs so Bitmap can move
to a mainline module. It is valuable to provide a public way for clients
to create a Bitmap backed by ashmem, and the method's usage level is
HIGH. As I understand it, ashmem is being deprecated, so make the new
name more general: #asShared(). This method calls createAshmemBitmap(),
which must continue to exist for now due to the greylist. Add a
maxTargetSdk and publicAlternatives to move clients to the new method.

Mark asShared as @NonNull. It should only fail due to a failure to
allocate the shared memory. Throw a RunTimeException in this (rare)
case.

Add a (private) native method to check whether the Bitmap is already
backed by ashmem so it can skip the copy as necessary.

Remove outdated comments in both createAshmemBitmap methods regarding
SRGB. The new Bitmap will have the same ColorSpace as the original.

Remove the Config param from DisplayContent#screenshotLocked. It is only
ever called with ARGB_8888, which matches the ScreenshotGraphicBuffer
it is copied from. This means Bitmap#createAshmemBitmap(Config) does not
need a public version. It may need to continue to exist due to
@UnsupportedAppUsage.

Change-Id: I359187a5c70b5e241c7f5879d50fde2a7449c818
diff --git a/api/current.txt b/api/current.txt
index 3122fd0..f7bb693 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14037,6 +14037,7 @@
 package android.graphics {
 
   public final class Bitmap implements android.os.Parcelable {
+    method @NonNull public android.graphics.Bitmap asShared();
     method @WorkerThread public boolean compress(android.graphics.Bitmap.CompressFormat, int, java.io.OutputStream);
     method public android.graphics.Bitmap copy(android.graphics.Bitmap.Config, boolean);
     method public void copyPixelsFromBuffer(java.nio.Buffer);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 32e7d84..c3a3b13 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6731,7 +6731,7 @@
             if (mPicture != null &&
                 mPicture.isMutable() &&
                 mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
-                mPicture = mPicture.createAshmemBitmap();
+                mPicture = mPicture.asShared();
             }
             if (mBigLargeIcon != null) {
                 mBigLargeIcon.convertToAshmem();
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 5a4eef2..7ddd8ab 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -686,14 +686,16 @@
         return b;
     }
 
+    // FIXME: The maxTargetSdk should be R, once R is no longer set to
+    // CUR_DEVELOPMENT.
     /**
      * Creates a new immutable bitmap backed by ashmem which can efficiently
-     * be passed between processes. The bitmap is assumed to be in the sRGB
-     * color space.
+     * be passed between processes.
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #asShared()} instead")
     public Bitmap createAshmemBitmap() {
         checkRecycled("Can't copy a recycled bitmap");
         noteHardwareBitmapSlowCall();
@@ -706,9 +708,26 @@
     }
 
     /**
+     * Return an immutable bitmap backed by shared memory which can be
+     * efficiently passed between processes via Parcelable.
+     *
+     * <p>If this bitmap already meets these criteria it will return itself.
+     */
+    @NonNull
+    public Bitmap asShared() {
+        if (nativeIsBackedByAshmem(mNativePtr) && nativeIsImmutable(mNativePtr)) {
+            return this;
+        }
+        Bitmap shared = createAshmemBitmap();
+        if (shared == null) {
+            throw new RuntimeException("Failed to create shared Bitmap!");
+        }
+        return shared;
+    }
+
+    /**
      * Creates a new immutable bitmap backed by ashmem which can efficiently
-     * be passed between processes. The bitmap is assumed to be in the sRGB
-     * color space.
+     * be passed between processes.
      *
      * @hide
      */
@@ -2346,4 +2365,7 @@
 
     @CriticalNative
     private static native boolean nativeIsImmutable(long nativePtr);
+
+    @CriticalNative
+    private static native boolean nativeIsBackedByAshmem(long nativePtr);
 }
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index cc7182c..90412f4 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -467,7 +467,7 @@
         if ((mType == TYPE_BITMAP || mType == TYPE_ADAPTIVE_BITMAP) &&
             getBitmap().isMutable() &&
             getBitmap().getAllocationByteCount() >= MIN_ASHMEM_ICON_SIZE) {
-            setBitmap(getBitmap().createAshmemBitmap());
+            setBitmap(getBitmap().asShared());
         }
     }
 
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index f42b249..c4865e3 100755
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -1216,6 +1216,14 @@
     return bitmapHolder->bitmap().isImmutable() ? JNI_TRUE : JNI_FALSE;
 }
 
+static jboolean Bitmap_isBackedByAshmem(CRITICAL_JNI_PARAMS_COMMA jlong bitmapHandle) {
+    LocalScopedBitmap bitmapHolder(bitmapHandle);
+    if (!bitmapHolder.valid()) return JNI_FALSE;
+
+    return bitmapHolder->bitmap().pixelStorageType() == PixelStorageType::Ashmem ? JNI_TRUE
+                                                                                 : JNI_FALSE;
+}
+
 static void Bitmap_setImmutable(JNIEnv* env, jobject, jlong bitmapHandle) {
     LocalScopedBitmap bitmapHolder(bitmapHandle);
     if (!bitmapHolder.valid()) return;
@@ -1282,7 +1290,8 @@
     {   "nativeSetImmutable",       "(J)V", (void*)Bitmap_setImmutable},
 
     // ------------ @CriticalNative ----------------
-    {   "nativeIsImmutable",        "(J)Z", (void*)Bitmap_isImmutable}
+    {   "nativeIsImmutable",        "(J)Z", (void*)Bitmap_isImmutable},
+    {   "nativeIsBackedByAshmem",   "(J)Z", (void*)Bitmap_isBackedByAshmem}
 
 };
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index 811a8d9..4a9a846 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -126,7 +126,7 @@
         Bitmap picture = generateAdjustedHwBitmap(
                 image, mPreviewWidth, mPreviewHeight, matrix, paint, overlayColor);
 
-        mNotificationStyle.bigPicture(picture.createAshmemBitmap());
+        mNotificationStyle.bigPicture(picture.asShared());
 
         // Note, we can't use the preview for the small icon, since it is non-square
         float scale = (float) mIconSize / Math.min(imageWidth, imageHeight);
@@ -145,7 +145,7 @@
         // On the tablet, the large icon makes the notification appear as if it is clickable
         // (and on small devices, the large icon is not shown) so defer showing the large icon
         // until we compose the final post-save notification below.
-        mNotificationBuilder.setLargeIcon(icon.createAshmemBitmap());
+        mNotificationBuilder.setLargeIcon(icon.asShared());
         // But we still don't set it for the expanded view, allowing the smallIcon to show here.
         mNotificationStyle.bigLargeIcon((Bitmap) null);
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 98e3d07..d5a0e58 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4037,10 +4037,8 @@
     /**
      * Takes a snapshot of the display.  In landscape mode this grabs the whole screen.
      * In portrait mode, it grabs the full screenshot.
-     *
-     * @param config of the output bitmap
      */
-    Bitmap screenshotDisplayLocked(Bitmap.Config config) {
+    Bitmap screenshotDisplayLocked() {
         if (!mWmService.mPolicy.isScreenOn()) {
             if (DEBUG_SCREENSHOT) {
                 Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
@@ -4085,8 +4083,10 @@
 
         // Create a copy of the screenshot that is immutable and backed in ashmem.
         // This greatly reduces the overhead of passing the bitmap between processes.
-        final Bitmap ret = bitmap.createAshmemBitmap(config);
-        bitmap.recycle();
+        final Bitmap ret = bitmap.asShared();
+        if (ret != bitmap) {
+            bitmap.recycle();
+        }
         return ret;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3e36a9d..fa9f9e4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3588,7 +3588,7 @@
                 }
                 bm = null;
             } else {
-                bm = displayContent.screenshotDisplayLocked(Bitmap.Config.ARGB_8888);
+                bm = displayContent.screenshotDisplayLocked();
             }
         }