Make bitmap backed by native memory instead of java byte array
Test: refactoring CL. Existing unit tests still pass.
bug:27762775

Change-Id: Ic4e914b3a941c3d545f8ce9e320e638973df0e91
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 18c4ee3..6acb76d 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -127,12 +127,10 @@
     }
 };
 
-Bitmap::Bitmap(JNIEnv* env, jbyteArray storageObj, void* address,
-            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
-        : mPixelStorageType(PixelStorageType::Java) {
-    env->GetJavaVM(&mPixelStorage.java.jvm);
-    mPixelStorage.java.jweakRef = env->NewWeakGlobalRef(storageObj);
-    mPixelStorage.java.jstrongRef = nullptr;
+Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
+        : mPixelStorageType(PixelStorageType::Heap) {
+    mPixelStorage.heap.address = address;
+    mPixelStorage.heap.size = size;
     mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable));
     // Note: this will trigger a call to onStrongRefDestroyed(), but
     // we want the pixel ref to have a ref count of 0 at this point
@@ -187,12 +185,8 @@
         munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
         close(mPixelStorage.ashmem.fd);
         break;
-    case PixelStorageType::Java:
-        JNIEnv* env = jniEnv();
-        LOG_ALWAYS_FATAL_IF(mPixelStorage.java.jstrongRef,
-                "Deleting a bitmap wrapper while there are outstanding strong "
-                "references! mPinnedRefCount = %d", mPinnedRefCount);
-        env->DeleteWeakGlobalRef(mPixelStorage.java.jweakRef);
+    case PixelStorageType::Heap:
+        free(mPixelStorage.heap.address);
         break;
     }
 
@@ -219,6 +213,15 @@
     }
 }
 
+size_t Bitmap::getAllocationByteCount() const {
+    switch (mPixelStorageType) {
+    case PixelStorageType::Heap:
+        return mPixelStorage.heap.size;
+    default:
+        return rowBytes() * height();
+    }
+}
+
 const SkImageInfo& Bitmap::info() const {
     return mPixelRef->info();
 }
@@ -244,7 +247,6 @@
         // We just restored this from 0, pin the pixels and inc the strong count
         // Note that there *might be* an incoming onStrongRefDestroyed from whatever
         // last unref'd
-        pinPixelsLocked();
         mPinnedRefCount++;
     }
     return mPixelRef.get();
@@ -283,13 +285,6 @@
     return mPinnedRefCount == 0 && !mAttachedToJava;
 }
 
-JNIEnv* Bitmap::jniEnv() {
-    JNIEnv* env;
-    auto success = mPixelStorage.java.jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
-    LOG_ALWAYS_FATAL_IF(success != JNI_OK,
-        "Failed to get JNIEnv* from JVM: %p", mPixelStorage.java.jvm);
-    return env;
-}
 
 void Bitmap::onStrongRefDestroyed() {
     bool disposeSelf = false;
@@ -298,7 +293,6 @@
         if (mPinnedRefCount > 0) {
             mPinnedRefCount--;
             if (mPinnedRefCount == 0) {
-                unpinPixelsLocked();
                 disposeSelf = shouldDisposeSelfLocked();
             }
         }
@@ -308,49 +302,6 @@
     }
 }
 
-void Bitmap::pinPixelsLocked() {
-    switch (mPixelStorageType) {
-    case PixelStorageType::Invalid:
-        LOG_ALWAYS_FATAL("Cannot pin invalid pixels!");
-        break;
-    case PixelStorageType::External:
-    case PixelStorageType::Ashmem:
-        // Nothing to do
-        break;
-    case PixelStorageType::Java: {
-        JNIEnv* env = jniEnv();
-        if (!mPixelStorage.java.jstrongRef) {
-            mPixelStorage.java.jstrongRef = reinterpret_cast<jbyteArray>(
-                    env->NewGlobalRef(mPixelStorage.java.jweakRef));
-            if (!mPixelStorage.java.jstrongRef) {
-                LOG_ALWAYS_FATAL("Failed to acquire strong reference to pixels");
-            }
-        }
-        break;
-    }
-    }
-}
-
-void Bitmap::unpinPixelsLocked() {
-    switch (mPixelStorageType) {
-    case PixelStorageType::Invalid:
-        LOG_ALWAYS_FATAL("Cannot unpin invalid pixels!");
-        break;
-    case PixelStorageType::External:
-    case PixelStorageType::Ashmem:
-        // Don't need to do anything
-        break;
-    case PixelStorageType::Java: {
-        JNIEnv* env = jniEnv();
-        if (mPixelStorage.java.jstrongRef) {
-            env->DeleteGlobalRef(mPixelStorage.java.jstrongRef);
-            mPixelStorage.java.jstrongRef = nullptr;
-        }
-        break;
-    }
-    }
-}
-
 void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
     assertValid();
     android::AutoMutex _lock(mLock);
@@ -723,7 +674,7 @@
     SkBitmap bitmap;
     bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType));
 
-    Bitmap* nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
+    Bitmap* nativeBitmap = GraphicsJNI::allocateHeapPixelRef(&bitmap, NULL);
     if (!nativeBitmap) {
         return NULL;
     }
@@ -742,8 +693,8 @@
     SkBitmap src;
     reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
     SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle);
-    SkBitmap            result;
-    JavaPixelAllocator  allocator(env);
+    SkBitmap result;
+    HeapAllocator allocator;
 
     if (!src.copyTo(&result, dstCT, &allocator)) {
         return NULL;
@@ -798,8 +749,7 @@
 }
 
 static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
-        jint width, jint height, jint configHandle, jint allocSize,
-        jboolean requestPremul) {
+        jint width, jint height, jint configHandle, jboolean requestPremul) {
     LocalScopedBitmap bitmap(bitmapHandle);
     SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle);
 
@@ -807,8 +757,8 @@
     if (colorType == kARGB_4444_SkColorType) {
         colorType = kN32_SkColorType;
     }
-
-    if (width * height * SkColorTypeBytesPerPixel(colorType) > allocSize) {
+    size_t requestedSize = width * height * SkColorTypeBytesPerPixel(colorType);
+    if (requestedSize > bitmap->getAllocationByteCount()) {
         // done in native as there's no way to get BytesPerPixel in Java
         doThrowIAE(env, "Bitmap not large enough to support new configuration");
         return;
@@ -1053,7 +1003,7 @@
 #endif
 
         // Copy the pixels into a new buffer.
-        nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable);
+        nativeBitmap = GraphicsJNI::allocateHeapPixelRef(bitmap.get(), ctable);
         SkSafeUnref(ctable);
         if (!nativeBitmap) {
             blob.release();
@@ -1165,7 +1115,7 @@
     const android::Paint* paint = reinterpret_cast<android::Paint*>(paintHandle);
     SkIPoint  offset;
     SkBitmap dst;
-    JavaPixelAllocator allocator(env);
+    HeapAllocator allocator;
 
     src.extractAlpha(&dst, paint, &allocator, &offset);
     // If Skia can't allocate pixels for destination bitmap, it resets
@@ -1370,6 +1320,11 @@
     android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmap);
 }
 
+static jint Bitmap_getAllocationByteCount(JNIEnv* env, jobject, jlong bitmapPtr) {
+    LocalScopedBitmap bitmapHandle(bitmapPtr);
+    return static_cast<jint>(bitmapHandle->getAllocationByteCount());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gBitmapMethods[] = {
@@ -1383,7 +1338,7 @@
         (void*)Bitmap_copyAshmemConfig },
     {   "nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer },
     {   "nativeRecycle",            "(J)Z", (void*)Bitmap_recycle },
-    {   "nativeReconfigure",        "(JIIIIZ)V", (void*)Bitmap_reconfigure },
+    {   "nativeReconfigure",        "(JIIIZ)V", (void*)Bitmap_reconfigure },
     {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",
         (void*)Bitmap_compress },
     {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },
@@ -1414,6 +1369,7 @@
     {   "nativeSameAs",             "(JJ)Z", (void*)Bitmap_sameAs },
     {   "nativeRefPixelRef",        "(J)J", (void*)Bitmap_refPixelRef },
     {   "nativePrepareToDraw",      "(J)V", (void*)Bitmap_prepareToDraw },
+    {   "nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount },
 };
 
 int register_android_graphics_Bitmap(JNIEnv* env)
diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h
index aaea178..9ae1f3f 100644
--- a/core/jni/android/graphics/Bitmap.h
+++ b/core/jni/android/graphics/Bitmap.h
@@ -28,7 +28,7 @@
 enum class PixelStorageType {
     Invalid,
     External,
-    Java,
+    Heap,
     Ashmem,
 };
 
@@ -47,8 +47,8 @@
  */
 class Bitmap {
 public:
-    Bitmap(JNIEnv* env, jbyteArray storageObj, void* address,
-            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
+    Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes,
+            SkColorTable* ctable);
     Bitmap(void* address, void* context, FreeFunc freeFunc,
             const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
     Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
@@ -56,12 +56,6 @@
 
     const SkImageInfo& info() const;
 
-    // Returns nullptr if it is not backed by a jbyteArray
-    jbyteArray javaByteArray() const {
-        return mPixelStorageType == PixelStorageType::Java
-                ? mPixelStorage.java.jstrongRef : nullptr;
-    }
-
     int width() const { return info().width(); }
     int height() const { return info().height(); }
     size_t rowBytes() const;
@@ -81,6 +75,7 @@
     bool hasHardwareMipMap();
     void setHasHardwareMipMap(bool hasMipMap);
     int getAshmemFd() const;
+    size_t getAllocationByteCount() const;
 
 private:
     friend class WrappedPixelRef;
@@ -90,8 +85,6 @@
     void onStrongRefDestroyed();
 
     void pinPixelsLocked();
-    void unpinPixelsLocked();
-    JNIEnv* jniEnv();
     bool shouldDisposeSelfLocked();
     void assertValid() const;
     SkPixelRef* refPixelRefLocked();
@@ -114,10 +107,9 @@
             size_t size;
         } ashmem;
         struct {
-            JavaVM* jvm;
-            jweak jweakRef;
-            jbyteArray jstrongRef;
-        } java;
+            void* address;
+            size_t size;
+        } heap;
     } mPixelStorage;
 };
 
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 77799d6..5a540ce 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -193,7 +193,7 @@
         bitmap->setPixelRef(mBitmap->refPixelRef())->unref();
 
         // since we're already allocated, we lockPixels right away
-        // HeapAllocator/JavaPixelAllocator behaves this way too
+        // HeapAllocator behaves this way too
         bitmap->lockPixels();
         return true;
     }
@@ -339,7 +339,7 @@
         }
     }
 
-    JavaPixelAllocator javaAllocator(env);
+    HeapAllocator defaultAllocator;
     RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize);
     ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
     SkBitmap::HeapAllocator heapAllocator;
@@ -353,10 +353,10 @@
         decodeAllocator = &recyclingAllocator;
     } else if (willScale) {
         // This will allocate pixels using a HeapAllocator, since there will be an extra
-        // scaling step that copies these pixels into Java memory.
+        // scaling step.
         decodeAllocator = &heapAllocator;
     } else {
-        decodeAllocator = &javaAllocator;
+        decodeAllocator = &defaultAllocator;
     }
 
     // Set the decode colorType.  This is necessary because we can't always support
@@ -412,7 +412,7 @@
 
     // Use SkAndroidCodec to perform the decode.
     SkAndroidCodec::AndroidOptions codecOptions;
-    codecOptions.fZeroInitialized = (decodeAllocator == &javaAllocator) ?
+    codecOptions.fZeroInitialized =  decodeAllocator == &defaultAllocator ?
             SkCodec::kYes_ZeroInitialized : SkCodec::kNo_ZeroInitialized;
     codecOptions.fColorPtr = colorPtr;
     codecOptions.fColorCount = colorCount;
@@ -477,7 +477,7 @@
         if (javaBitmap != nullptr) {
             outputAllocator = &recyclingAllocator;
         } else {
-            outputAllocator = &javaAllocator;
+            outputAllocator = &defaultAllocator;
         }
 
         SkColorType scaledColorType = colorTypeForScaledOutput(decodingBitmap.colorType());
@@ -540,7 +540,7 @@
     if (isPremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
 
     // now create the java bitmap
-    return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(),
+    return GraphicsJNI::createBitmap(env, defaultAllocator.getStorageObjAndReset(),
             bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
 }
 
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 970001a..21850bd 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -161,13 +161,13 @@
     // Set up the pixel allocator
     SkBRDAllocator* allocator = nullptr;
     RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes);
-    JavaPixelAllocator javaAlloc(env);
+    HeapAllocator heapAlloc;
     if (javaBitmap) {
         allocator = &recycleAlloc;
         // We are required to match the color type of the recycled bitmap.
         colorType = recycledBitmap->info().colorType();
     } else {
-        allocator = &javaAlloc;
+        allocator = &heapAlloc;
     }
 
     // Decode the region.
@@ -200,7 +200,7 @@
     if (!requireUnpremul) {
         bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
     }
-    return GraphicsJNI::createBitmap(env, javaAlloc.getStorageObjAndReset(), bitmapCreateFlags);
+    return GraphicsJNI::createBitmap(env, heapAlloc.getStorageObjAndReset(), bitmapCreateFlags);
 }
 
 static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index c6a51e8..8cee814 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -416,9 +416,8 @@
     assert_premultiplied(bitmap->info(), isPremultiplied);
 
     jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
-            reinterpret_cast<jlong>(bitmap), bitmap->javaByteArray(),
-            bitmap->width(), bitmap->height(), density, isMutable, isPremultiplied,
-            ninePatchChunk, ninePatchInsets);
+            reinterpret_cast<jlong>(bitmap), bitmap->width(), bitmap->height(), density, isMutable,
+            isPremultiplied, ninePatchChunk, ninePatchInsets);
     hasException(env); // For the side effect of logging.
     return obj;
 }
@@ -483,37 +482,28 @@
     return true;
 }
 
-android::Bitmap* GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
-                                             SkColorTable* ctable) {
+android::Bitmap* GraphicsJNI::allocateHeapPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
     const SkImageInfo& info = bitmap->info();
     if (info.colorType() == kUnknown_SkColorType) {
-        doThrowIAE(env, "unknown bitmap configuration");
-        return NULL;
+        LOG_ALWAYS_FATAL("unknown bitmap configuration");
+        return nullptr;
     }
 
     size_t size;
     if (!computeAllocationSize(*bitmap, &size)) {
-        return NULL;
+        return nullptr;
     }
 
     // we must respect the rowBytes value already set on the bitmap instead of
     // attempting to compute our own.
     const size_t rowBytes = bitmap->rowBytes();
 
-    jbyteArray arrayObj = (jbyteArray) env->CallObjectMethod(gVMRuntime,
-                                                             gVMRuntime_newNonMovableArray,
-                                                             gByte_class, size);
-    if (env->ExceptionCheck() != 0) {
-        return NULL;
+    void* addr = calloc(size, 1);
+    if (!addr) {
+        return nullptr;
     }
-    SkASSERT(arrayObj);
-    jbyte* addr = (jbyte*) env->CallLongMethod(gVMRuntime, gVMRuntime_addressOf, arrayObj);
-    if (env->ExceptionCheck() != 0) {
-        return NULL;
-    }
-    SkASSERT(addr);
-    android::Bitmap* wrapper = new android::Bitmap(env, arrayObj, (void*) addr,
-            info, rowBytes, ctable);
+
+    android::Bitmap* wrapper = new android::Bitmap(addr, size, info, rowBytes, ctable);
     wrapper->getSkBitmap(bitmap);
     // since we're already allocated, we lockPixels right away
     // HeapAllocator behaves this way too
@@ -658,21 +648,16 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) {
-    LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJavaVM) != JNI_OK,
-            "env->GetJavaVM failed");
-}
+HeapAllocator::HeapAllocator() {}
 
-JavaPixelAllocator::~JavaPixelAllocator() {
+HeapAllocator::~HeapAllocator() {
     if (mStorage) {
         mStorage->detachFromJava();
     }
 }
 
-bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
-    JNIEnv* env = vm2env(mJavaVM);
-
-    mStorage = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
+bool HeapAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
+    mStorage = GraphicsJNI::allocateHeapPixelRef(bitmap, ctable);
     return mStorage != nullptr;
 }
 
@@ -830,7 +815,7 @@
 
     gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
     gBitmap_nativePtr = getFieldIDCheck(env, gBitmap_class, "mNativePtr", "J");
-    gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(J[BIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V");
+    gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(JIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V");
     gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "(IIZ)V");
     gBitmap_getAllocationByteCountMethodID = env->GetMethodID(gBitmap_class, "getAllocationByteCount", "()I");
     gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 89636db..738ad54 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -93,8 +93,7 @@
 
     static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
 
-    static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
-            SkColorTable* ctable);
+    static android::Bitmap* allocateHeapPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
 
     static android::Bitmap* allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
             SkColorTable* ctable);
@@ -119,15 +118,10 @@
             const SkBitmap& dstBitmap);
 };
 
-/** Allocator which allocates the backing buffer in the Java heap.
- *  Instances can only be used to perform a single allocation, which helps
- *  ensure that the allocated buffer is properly accounted for with a
- *  reference in the heap (or a JNI global reference).
- */
-class JavaPixelAllocator : public SkBRDAllocator {
+class HeapAllocator : public SkBRDAllocator {
 public:
-    explicit JavaPixelAllocator(JNIEnv* env);
-    ~JavaPixelAllocator();
+   HeapAllocator();
+    ~HeapAllocator();
 
     virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) override;
 
@@ -140,14 +134,8 @@
         return result;
     };
 
-    /**
-     *  Indicates that this allocator allocates zero initialized
-     *  memory.
-     */
     SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kYes_ZeroInitialized; }
-
 private:
-    JavaVM* mJavaVM;
     android::Bitmap* mStorage = nullptr;
 };
 
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 49721cf..7ce750d 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -48,11 +48,6 @@
     // pixel data.
     private static final long NATIVE_ALLOCATION_SIZE = 32;
 
-    /**
-     * Backing buffer for the Bitmap.
-     */
-    private byte[] mBuffer;
-
     // Convenience for JNI access
     private final long mNativePtr;
 
@@ -108,7 +103,7 @@
      * int (pointer).
      */
     // called from JNI
-    Bitmap(long nativeBitmap, byte[] buffer, int width, int height, int density,
+    Bitmap(long nativeBitmap, int width, int height, int density,
             boolean isMutable, boolean requestPremultiplied,
             byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets) {
         if (nativeBitmap == 0) {
@@ -119,7 +114,6 @@
         mHeight = height;
         mIsMutable = isMutable;
         mRequestPremultiplied = requestPremultiplied;
-        mBuffer = buffer;
 
         mNinePatchChunk = ninePatchChunk;
         mNinePatchInsets = ninePatchInsets;
@@ -128,10 +122,7 @@
         }
 
         mNativePtr = nativeBitmap;
-        long nativeSize = NATIVE_ALLOCATION_SIZE;
-        if (buffer == null) {
-            nativeSize += getByteCount();
-        }
+        long nativeSize = NATIVE_ALLOCATION_SIZE + getAllocationByteCount();
         NativeAllocationRegistry registry = new NativeAllocationRegistry(
             Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), nativeSize);
         registry.registerNativeAllocation(this, nativeBitmap);
@@ -256,12 +247,8 @@
         if (!isMutable()) {
             throw new IllegalStateException("only mutable bitmaps may be reconfigured");
         }
-        if (mBuffer == null) {
-            throw new IllegalStateException("native-backed bitmaps may not be reconfigured");
-        }
 
-        nativeReconfigure(mNativePtr, width, height, config.nativeInt,
-                mBuffer.length, mRequestPremultiplied);
+        nativeReconfigure(mNativePtr, width, height, config.nativeInt, mRequestPremultiplied);
         mWidth = width;
         mHeight = height;
     }
@@ -343,7 +330,6 @@
                 // false indicates that it is still in use at the native level and these
                 // objects should not be collected now. They will be collected later when the
                 // Bitmap itself is collected.
-                mBuffer = null;
                 mNinePatchChunk = null;
             }
             mRecycled = true;
@@ -1273,12 +1259,7 @@
      * @see #reconfigure(int, int, Config)
      */
     public final int getAllocationByteCount() {
-        if (mBuffer == null) {
-            // native backed bitmaps don't support reconfiguration,
-            // so alloc size is always content size
-            return getByteCount();
-        }
-        return mBuffer.length;
+        return nativeGetAllocationByteCount(mNativePtr);
     }
 
     /**
@@ -1695,8 +1676,7 @@
     private static native long nativeGetNativeFinalizer();
     private static native boolean nativeRecycle(long nativeBitmap);
     private static native void nativeReconfigure(long nativeBitmap, int width, int height,
-                                                 int config, int allocSize,
-                                                 boolean isPremultiplied);
+                                                 int config, boolean isPremultiplied);
 
     private static native boolean nativeCompress(long nativeBitmap, int format,
                                             int quality, OutputStream stream,
@@ -1742,4 +1722,5 @@
     private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1);
     private static native long nativeRefPixelRef(long nativeBitmap);
     private static native void nativePrepareToDraw(long nativeBitmap);
+    private static native int nativeGetAllocationByteCount(long nativeBitmap);
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index f1da3a2..bcfe3bf 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -627,7 +627,7 @@
         boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED);
 
         // and create/return a new Bitmap with it
-        return new Bitmap(nativeInt, null /* buffer */, width, height, density, isMutable,
+        return new Bitmap(nativeInt, width, height, density, isMutable,
                           isPremultiplied, null /*ninePatchChunk*/, null /* layoutBounds */);
     }