Merge "Allocate bitmap backing buffers in the Java heap."
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 5d9bd1e..773d734 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -459,35 +459,37 @@
         // Shaders are ignored when drawing patches
         boolean hasColorFilter = paint != null && setupColorFilter(paint);
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawPatch(mRenderer, bitmap.mNativeBitmap, chunks, dst.left, dst.top,
-                dst.right, dst.bottom, nativePaint);
+        nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks,
+                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
         if (hasColorFilter) nResetModifiers(mRenderer);
     }
 
-    private native void nDrawPatch(int renderer, int bitmap, byte[] chunks, float left, float top,
-            float right, float bottom, int paint);
+    private native void nDrawPatch(int renderer, int bitmap, byte[] buffer, byte[] chunks,
+            float left, float top, float right, float bottom, int paint);
 
     @Override
     public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
         // Shaders are ignored when drawing bitmaps
         boolean hasColorFilter = paint != null && setupColorFilter(paint);
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint);
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
         if (hasColorFilter) nResetModifiers(mRenderer);
     }
 
-    private native void nDrawBitmap(int renderer, int bitmap, float left, float top, int paint);
+    private native void nDrawBitmap(
+            int renderer, int bitmap, byte[] buffer, float left, float top, int paint);
 
     @Override
     public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
         // Shaders are ignored when drawing bitmaps
         boolean hasColorFilter = paint != null && setupColorFilter(paint);
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint);
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
+                matrix.native_instance, nativePaint);
         if (hasColorFilter) nResetModifiers(mRenderer);
     }
 
-    private native void nDrawBitmap(int renderer, int bitmap, int matrix, int paint);
+    private native void nDrawBitmap(int renderer, int bitmap, byte[] buff, int matrix, int paint);
 
     @Override
     public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
@@ -507,7 +509,7 @@
             bottom = src.bottom;
         }
 
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint);
         if (hasColorFilter) nResetModifiers(mRenderer);
     }
@@ -517,12 +519,12 @@
         // Shaders are ignored when drawing bitmaps
         boolean hasColorFilter = paint != null && setupColorFilter(paint);
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
-                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, src.left, src.top, src.right,
+                src.bottom, dst.left, dst.top, dst.right, dst.bottom, nativePaint);
         if (hasColorFilter) nResetModifiers(mRenderer);
     }
 
-    private native void nDrawBitmap(int renderer, int bitmap,
+    private native void nDrawBitmap(int renderer, int bitmap, byte[] buffer,
             float srcLeft, float srcTop, float srcRight, float srcBottom,
             float left, float top, float right, float bottom, int paint);
 
@@ -534,7 +536,7 @@
         final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
         final Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config);
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawBitmap(mRenderer, b.mNativeBitmap, x, y, nativePaint);
+        nDrawBitmap(mRenderer, b.mNativeBitmap, b.mBuffer, x, y, nativePaint);
         b.recycle();
         if (hasColorFilter) nResetModifiers(mRenderer);
     }
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 29c6ba2..1d961ec 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1,4 +1,5 @@
 #include "SkBitmap.h"

+#include "SkPixelRef.h"

 #include "SkImageEncoder.h"

 #include "SkColorPriv.h"

 #include "GraphicsJNI.h"

@@ -210,11 +211,6 @@
 static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,

                               int offset, int stride, int width, int height,

                               SkBitmap::Config config, jboolean isMutable) {

-    if (width <= 0 || height <= 0) {

-        doThrowIAE(env, "width and height must be > 0");

-        return NULL;

-    }

-

     if (NULL != jColors) {

         size_t n = env->GetArrayLength(jColors);

         if (n < SkAbs32(stride) * (size_t)height) {

@@ -226,7 +222,9 @@
     SkBitmap bitmap;

 

     bitmap.setConfig(config, width, height);

-    if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL, true)) {

+

+    jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);

+    if (NULL == buff) {

         return NULL;

     }

 

@@ -235,21 +233,19 @@
                                0, 0, width, height, bitmap);

     }

 

-    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), isMutable,

-                                     NULL);

+    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL);

 }

 

 static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,

                            SkBitmap::Config dstConfig, jboolean isMutable) {

     SkBitmap            result;

-    JavaPixelAllocator  allocator(env, true);

+    JavaPixelAllocator  allocator(env);

 

     if (!src->copyTo(&result, dstConfig, &allocator)) {

         return NULL;

     }

 

-    return GraphicsJNI::createBitmap(env, new SkBitmap(result), isMutable,

-                                     NULL);

+    return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL);

 }

 

 static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {

@@ -380,7 +376,8 @@
         }

     }

 

-    if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable, true)) {

+    jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);

+    if (NULL == buffer) {

         ctable->safeUnref();

         delete bitmap;

         return NULL;

@@ -393,7 +390,7 @@
     memcpy(bitmap->getPixels(), p->readInplace(size), size);

     bitmap->unlockPixels();

 

-    return GraphicsJNI::createBitmap(env, bitmap, isMutable, NULL, density);

+    return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, density);

 }

 

 static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,

@@ -447,8 +444,9 @@
                                    jintArray offsetXY) {

     SkIPoint  offset;

     SkBitmap* dst = new SkBitmap;

+    JavaPixelAllocator allocator(env);

 

-    src->extractAlpha(dst, paint, &offset);

+    src->extractAlpha(dst, paint, &allocator, &offset);

     if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {

         int* array = env->GetIntArrayElements(offsetXY, NULL);

         array[0] = offset.fX;

@@ -456,7 +454,7 @@
         env->ReleaseIntArrayElements(offsetXY, array, 0);

     }

 

-    return GraphicsJNI::createBitmap(env, dst, true, NULL);

+    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL);

 }

 

 ///////////////////////////////////////////////////////////////////////////////

diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 164b357..73c9a50 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -291,7 +291,7 @@
         env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
     }
 
-    // detach bitmap from its autotdeleter, since we want to own it now
+    // detach bitmap from its autodeleter, since we want to own it now
     adb.detach();
 
     if (padding) {
@@ -322,7 +322,7 @@
         return javaBitmap;
     }
     // now create the java bitmap
-    return GraphicsJNI::createBitmap(env, bitmap, false, ninePatchChunk);
+    return GraphicsJNI::createBitmap(env, bitmap, javaAllocator.getStorageObj(), false, ninePatchChunk);
 }
 
 static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 91a8202..1e00b71 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -79,7 +79,7 @@
         return nullObjectReturn("SkImageDecoder::Factory returned null");
     }
 
-    JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
+    JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env);
     decoder->setAllocator(javaAllocator);
     JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
     decoder->setReporter(javaMemoryReporter);
@@ -241,15 +241,16 @@
                             getMimeTypeString(env, decoder->getFormat()));
     }
 
-    // detach bitmap from its autotdeleter, since we want to own it now
+    // detach bitmap from its autodeleter, since we want to own it now
     adb.detach();
 
-    SkPixelRef* pr;
-    pr = bitmap->pixelRef();
+    SkPixelRef* pr = bitmap->pixelRef();
     // promise we will never change our pixels (great for sharing and pictures)
     pr->setImmutable();
+
     // now create the java bitmap
-    return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
+    jbyteArray buff = ((AndroidPixelRef*) pr)->getStorageObj();
+    return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, -1);
 }
 
 static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 9467be8..fc358a1 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -2,6 +2,9 @@
 
 #include "jni.h"
 #include "GraphicsJNI.h"
+
+#include "SkCanvas.h"
+#include "SkDevice.h"
 #include "SkPicture.h"
 #include "SkRegion.h"
 #include <android_runtime/AndroidRuntime.h>
@@ -163,7 +166,6 @@
 static jclass   gBitmap_class;
 static jfieldID gBitmap_nativeInstanceID;
 static jmethodID gBitmap_constructorMethodID;
-static jmethodID gBitmap_allocBufferMethodID;
 
 static jclass   gBitmapConfig_class;
 static jfieldID gBitmapConfig_nativeInstanceID;
@@ -360,16 +362,16 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////
 
-jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
-                                  jbyteArray ninepatch, int density)
+jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
+                                  bool isMutable, jbyteArray ninepatch, int density)
 {
-    SkASSERT(bitmap != NULL);
-    SkASSERT(NULL != bitmap->pixelRef());
+    SkASSERT(bitmap);
+    SkASSERT(bitmap->pixelRef());
     
     jobject obj = env->AllocObject(gBitmap_class);
     if (obj) {
         env->CallVoidMethod(obj, gBitmap_constructorMethodID,
-                            (jint)bitmap, isMutable, ninepatch, density);
+                            (jint)bitmap, buffer, isMutable, ninepatch, density);
         if (hasException(env)) {
             obj = NULL;
         }
@@ -377,6 +379,13 @@
     return obj;
 }
 
+jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
+                            jbyteArray ninepatch, int density)
+{
+    return createBitmap(env, bitmap, NULL, isMutable, ninepatch, density);
+}
+
+
 jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
 {
     SkASSERT(bitmap != NULL);
@@ -408,8 +417,6 @@
     return obj;
 }
 
-#include "SkPixelRef.h"
-
 static JNIEnv* vm2env(JavaVM* vm)
 {
     JNIEnv* env = NULL;
@@ -427,87 +434,125 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#include "SkMallocPixelRef.h"
+AndroidPixelRef::AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteArray storageObj,
+        SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) {
+    SkASSERT(storage);
+    SkASSERT(env);
 
-/*  Extend SkMallocPixelRef to inform the VM when we free up the storage
-*/
-class AndroidPixelRef : public SkMallocPixelRef {
-public:
-    /** Allocate the specified buffer for pixels. The memory is freed when the
-        last owner of this pixelref is gone. Our caller has already informed
-        the VM of our allocation.
-    */
-    AndroidPixelRef(JNIEnv* env, void* storage, size_t size,
-            SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) {
-        SkASSERT(storage);
-        SkASSERT(env);
-
-        if (env->GetJavaVM(&fVM) != JNI_OK) {
-            SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
-            sk_throw();
-        }        
+    if (env->GetJavaVM(&fVM) != JNI_OK) {
+        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
+        sk_throw();
     }
+    fStorageObj = storageObj;
+    fHasGlobalRef = false;
+    fGlobalRefCnt = 0;
 
-    virtual ~AndroidPixelRef() {
+    // If storageObj is NULL, the memory was NOT allocated on the Java heap
+    fOnJavaHeap = (storageObj != NULL);
+    
+}
+
+AndroidPixelRef::~AndroidPixelRef() {
+    if (fOnJavaHeap) {
         JNIEnv* env = vm2env(fVM);
-//        SkDebugf("-------------- inform VM we're releasing %d bytes\n", this->getSize());
-        jlong jsize = this->getSize();  // the VM wants longs for the size
-        env->CallVoidMethod(gVMRuntime_singleton,
-                            gVMRuntime_trackExternalFreeMethodID,
-                            jsize);
-        if (GraphicsJNI::hasException(env)) {
-            env->ExceptionClear();
+
+        if (fStorageObj && fHasGlobalRef) {
+            env->DeleteGlobalRef(fStorageObj);
+        }
+        fStorageObj = NULL;
+
+        // Set this to NULL to prevent the SkMallocPixelRef destructor
+        // from freeing the memory.
+        fStorage = NULL;
+    }
+}
+
+void AndroidPixelRef::setLocalJNIRef(jbyteArray arr) {
+    if (!fHasGlobalRef) {
+        fStorageObj = arr;
+    }
+}
+
+void AndroidPixelRef::globalRef() {
+    if (fOnJavaHeap && sk_atomic_inc(&fGlobalRefCnt) == 0) {
+        JNIEnv *env = vm2env(fVM);
+        if (fStorageObj == NULL) {
+            SkDebugf("Cannot create a global ref, fStorage obj is NULL");
+            sk_throw();
+        }
+        if (fHasGlobalRef) {
+            // This should never happen
+            SkDebugf("Already holding a global ref");
+            sk_throw();
+        }
+
+        fStorageObj = (jbyteArray) env->NewGlobalRef(fStorageObj);
+        // TODO: Check for failure here
+        fHasGlobalRef = true;
+    }
+    ref();
+}
+
+void AndroidPixelRef::globalUnref() {
+    if (fOnJavaHeap && sk_atomic_dec(&fGlobalRefCnt) == 1) {
+        JNIEnv *env = vm2env(fVM);
+        if (!fHasGlobalRef) {
+            SkDebugf("We don't have a global ref!");
+            sk_throw();
+        }
+        env->DeleteGlobalRef(fStorageObj);
+        fStorageObj = NULL;
+        fHasGlobalRef = false;
+    }
+    unref();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
+                                             SkColorTable* ctable) {
+    Sk64 size64 = bitmap->getSize64();
+    if (size64.isNeg() || !size64.is32()) {
+        doThrow(env, "java/lang/IllegalArgumentException",
+                     "bitmap size exceeds 32bits");
+        return NULL;
+    }
+    
+    size_t size = size64.get32();
+    jbyteArray arrayObj = env->NewByteArray(size);
+    if (arrayObj) {
+        jbyte *addr = env->GetByteArrayElements(arrayObj, NULL);
+        env->ReleaseByteArrayElements(arrayObj, addr, 0);
+        if (addr) {
+            SkPixelRef* pr = new AndroidPixelRef(env, (void*) addr, size, arrayObj, ctable);
+            bitmap->setPixelRef(pr)->unref();
+            // since we're already allocated, we lockPixels right away
+            // HeapAllocator behaves this way too
+            bitmap->lockPixels();
         }
     }
 
-private:
-    JavaVM* fVM;
-};
+    return arrayObj;
+}
 
-bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
-                                  SkColorTable* ctable, bool reportSizeToVM) {
+bool GraphicsJNI::mallocPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) {
     Sk64 size64 = bitmap->getSize64();
     if (size64.isNeg() || !size64.is32()) {
         doThrow(env, "java/lang/IllegalArgumentException",
                      "bitmap size exceeds 32bits");
         return false;
     }
-    
+
     size_t size = size64.get32();
-    jlong jsize = size;  // the VM wants longs for the size
-    if (reportSizeToVM) {
-        //    SkDebugf("-------------- inform VM we've allocated %d bytes\n", size);
-        bool r = env->CallBooleanMethod(gVMRuntime_singleton,
-                                    gVMRuntime_trackExternalAllocationMethodID,
-                                    jsize);
-        if (GraphicsJNI::hasException(env)) {
-            return false;
-        }
-        if (!r) {
-            LOGE("VM won't let us allocate %zd bytes\n", size);
-            doThrowOOME(env, "bitmap size exceeds VM budget");
-            return false;
-        }
-    }
+
     // call the version of malloc that returns null on failure
     void* addr = sk_malloc_flags(size, 0);
+
     if (NULL == addr) {
-        if (reportSizeToVM) {
-            //        SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size);
-            // we didn't actually allocate it, so inform the VM
-            env->CallVoidMethod(gVMRuntime_singleton,
-                                 gVMRuntime_trackExternalFreeMethodID,
-                                 jsize);
-            if (!GraphicsJNI::hasException(env)) {
-                doThrowOOME(env, "bitmap size too large for malloc");
-            }
-        }
         return false;
     }
-    
-    SkPixelRef* pr = reportSizeToVM ?
-                        new AndroidPixelRef(env, addr, size, ctable) :
-                        new SkMallocPixelRef(addr, size, ctable);
+
+    SkPixelRef* pr = new AndroidPixelRef(env, addr, size, NULL, ctable);
     bitmap->setPixelRef(pr)->unref();
     // since we're already allocated, we lockPixels right away
     // HeapAllocator behaves this way too
@@ -517,8 +562,9 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM)
-    : fReportSizeToVM(reportSizeToVM) {
+JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool allocateInJavaHeap)
+    : fAllocateInJavaHeap(allocateInJavaHeap),
+      fStorageObj(NULL) {
     if (env->GetJavaVM(&fVM) != JNI_OK) {
         SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
         sk_throw();
@@ -527,7 +573,19 @@
     
 bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
     JNIEnv* env = vm2env(fVM);
-    return GraphicsJNI::setJavaPixelRef(env, bitmap, ctable, fReportSizeToVM);
+
+    // If allocating in the Java heap, only allow a single object to be
+    // allocated for the lifetime of this object.
+    if (fStorageObj != NULL) {
+        SkDebugf("ERROR: One-shot allocator has already allocated\n");
+        sk_throw();
+    }
+
+    if (fAllocateInJavaHeap) {
+        fStorageObj = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
+        return fStorageObj != NULL;
+    }
+    return GraphicsJNI::mallocPixelRef(env, bitmap, ctable);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -568,6 +626,25 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+JavaHeapBitmapRef::JavaHeapBitmapRef(JNIEnv* env, SkBitmap* nativeBitmap, jbyteArray buffer) {
+    fEnv = env;
+    fNativeBitmap = nativeBitmap;
+    fBuffer = buffer;
+
+    // If the buffer is NULL, the backing memory wasn't allocated on the Java heap
+    if (fBuffer) {
+        ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(fBuffer);
+    }
+}
+
+JavaHeapBitmapRef::~JavaHeapBitmapRef() {
+    if (fBuffer) {
+        ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(NULL);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 static jclass make_globalref(JNIEnv* env, const char classname[])
 {
     jclass c = env->FindClass(classname);
@@ -609,10 +686,9 @@
     gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F");
 
     gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
-    gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");    
+    gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");
     gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
-                                            "(IZ[BI)V");
-
+                                            "(I[BZ[BI)V");
     gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
     gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");
 
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index d0f9125..505d24e 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -1,9 +1,12 @@
 #ifndef GraphicsJNI_DEFINED
 #define GraphicsJNI_DEFINED
 
+#include "SkBitmap.h"
+#include "SkDevice.h"
+#include "SkPixelRef.h"
+#include "SkMallocPixelRef.h"
 #include "SkPoint.h"
 #include "SkRect.h"
-#include "SkBitmap.h"
 #include "../images/SkBitmapRegionDecoder.h"
 #include "../images/SkImageDecoder.h"
 #include <jni.h>
@@ -47,23 +50,26 @@
     static SkBitmap::Config getNativeBitmapConfig(JNIEnv*, jobject jconfig);
     
     /** Create a java Bitmap object given the native bitmap (required) and optional
-        storage array (may be null). If storage is specified, then it must already be
-        locked, and its native address set as the bitmap's pixels. If storage is null,
-        then the bitmap must be an owner of its natively allocated pixels (via allocPixels).
-        */
+        storage array (may be null).
+    */
+    static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
+                                bool isMutable, jbyteArray ninepatch, int density = -1);
+
     static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
-                                jbyteArray ninePatch, int density = -1);
+                                jbyteArray ninepatch, int density = -1);
     
     static jobject createRegion(JNIEnv* env, SkRegion* region);
 
     static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
 
+    static jbyteArray allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
+                                     SkColorTable* ctable);
+
     /** Set a pixelref for the bitmap (needs setConfig to already be called)
         Returns true on success. If it returns false, then it failed, and the
         appropriate exception will have been raised.
     */
-    static bool setJavaPixelRef(JNIEnv*, SkBitmap*, SkColorTable* ctable,
-                                bool reportSizeToVM);
+    static bool mallocPixelRef(JNIEnv*, SkBitmap*, SkColorTable* ctable);
 
     /** Copy the colors in colors[] to the bitmap, convert to the correct
         format along the way.
@@ -71,17 +77,71 @@
     static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset,
                           int srcStride, int x, int y, int width, int height,
                           const SkBitmap& dstBitmap);
+
+    static jbyteArray getBitmapStorageObj(SkPixelRef *pixref);
 };
 
+class AndroidPixelRef : public SkMallocPixelRef {
+public:
+    AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteArray storageObj,
+                    SkColorTable* ctable);
+
+    virtual ~AndroidPixelRef();
+
+    jbyteArray getStorageObj() { return fStorageObj; }
+
+    void setLocalJNIRef(jbyteArray arr);
+
+    virtual void globalRef();
+    virtual void globalUnref();
+
+private:
+    JavaVM* fVM;
+    bool fOnJavaHeap; // If true, the memory was allocated on the Java heap
+
+    jbyteArray fStorageObj; // The Java byte[] object used as the bitmap backing store
+    bool fHasGlobalRef; // If true, fStorageObj holds a JNI global ref
+
+    mutable int32_t fGlobalRefCnt;
+};
+
+/** A helper class for accessing Java-heap-allocated bitmaps.
+ *  This should be used when calling into a JNI method that retains a
+ *  reference to the bitmap longer than the lifetime of the Java Bitmap.
+ *
+ *  After creating an instance of this class, a call to
+ *  AndroidPixelRef::globalRef() will allocate a JNI global reference
+ *  to the backing buffer object.
+ */
+class JavaHeapBitmapRef {
+public:
+
+    JavaHeapBitmapRef(JNIEnv *env, SkBitmap* nativeBitmap, jbyteArray buffer);
+    ~JavaHeapBitmapRef();
+
+private:
+    JNIEnv* fEnv;
+    SkBitmap* fNativeBitmap;
+    jbyteArray fBuffer;
+};
+
+/** 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 SkBitmap::Allocator {
 public:
-    JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM);
+    JavaPixelAllocator(JNIEnv* env, bool allocateInJavaHeap=true);
     // overrides
     virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
     
+    jbyteArray getStorageObj() { return fStorageObj; };
+
 private:
     JavaVM* fVM;
-    bool fReportSizeToVM;
+    bool fAllocateInJavaHeap;
+    jbyteArray fStorageObj;
 };
 
 class JavaMemoryUsageReporter : public SkVMMemoryReporter {
diff --git a/core/jni/android_emoji_EmojiFactory.cpp b/core/jni/android_emoji_EmojiFactory.cpp
index f653b36..1ebb36c 100644
--- a/core/jni/android_emoji_EmojiFactory.cpp
+++ b/core/jni/android_emoji_EmojiFactory.cpp
@@ -185,7 +185,7 @@
   jobject obj = env->AllocObject(gBitmap_class);
   if (obj) {
     env->CallVoidMethod(obj, gBitmap_constructorMethodID,
-                        reinterpret_cast<jint>(bitmap), false, NULL, -1);
+                        reinterpret_cast<jint>(bitmap), NULL, false, NULL, -1);
     if (env->ExceptionCheck() != 0) {
       LOGE("*** Uncaught exception returned from Java call!\n");
       env->ExceptionDescribe();
@@ -298,7 +298,7 @@
 int register_android_emoji_EmojiFactory(JNIEnv* env) {
   gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
   gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
-                                                 "(IZ[BI)V");
+                                                 "(I[BZ[BI)V");
   gEmojiFactory_class = make_globalref(env, "android/emoji/EmojiFactory");
   gEmojiFactory_constructorMethodID = env->GetMethodID(
       gEmojiFactory_class, "<init>", "(ILjava/lang/String;)V");
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index cb261d4..c3d3572 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "OpenGLRenderer"
 
 #include "jni.h"
+#include "GraphicsJNI.h"
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/ResourceTypes.h>
@@ -66,8 +67,6 @@
 #endif
 
 // ----------------------------------------------------------------------------
-// Java APIs
-// ----------------------------------------------------------------------------
 
 static struct {
     jclass clazz;
@@ -238,32 +237,46 @@
 // ----------------------------------------------------------------------------
 
 static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject canvas,
-        OpenGLRenderer* renderer, SkBitmap* bitmap, float left, float top, SkPaint* paint) {
+        OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer, float left,
+        float top, SkPaint* paint) {
+    // This object allows the renderer to allocate a global JNI ref to the buffer object.
+    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
+
     renderer->drawBitmap(bitmap, left, top, paint);
 }
 
 static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject canvas,
-        OpenGLRenderer* renderer, SkBitmap* bitmap,
+        OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer,
         float srcLeft, float srcTop, float srcRight, float srcBottom,
         float dstLeft, float dstTop, float dstRight, float dstBottom, SkPaint* paint) {
+    // This object allows the renderer to allocate a global JNI ref to the buffer object.
+    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
+
     renderer->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
             dstLeft, dstTop, dstRight, dstBottom, paint);
 }
 
 static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject canvas,
-        OpenGLRenderer* renderer, SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
+        OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer, SkMatrix* matrix,
+        SkPaint* paint) {
+    // This object allows the renderer to allocate a global JNI ref to the buffer object.
+    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
+
     renderer->drawBitmap(bitmap, matrix, paint);
 }
 
 static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject canvas,
-        OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray chunks,
+        OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer, jbyteArray chunks,
         float left, float top, float right, float bottom, SkPaint* paint) {
+    // This object allows the renderer to allocate a global JNI ref to the buffer object.
+    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
+
     jbyte* storage = env->GetByteArrayElements(chunks, NULL);
     Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(storage);
     Res_png_9patch::deserialize(patch);
 
-    renderer->drawPatch(bitmap, &patch->xDivs[0], &patch->yDivs[0], &patch->colors[0],
-            patch->numXDivs, patch->numYDivs, patch->numColors,
+    renderer->drawPatch(bitmap, &patch->xDivs[0], &patch->yDivs[0],
+            &patch->colors[0], patch->numXDivs, patch->numYDivs, patch->numColors,
             left, top, right, bottom, paint);
 
     env->ReleaseByteArrayElements(chunks, storage, 0);
@@ -477,10 +490,11 @@
     { "nGetMatrix",         "(II)V",           (void*) android_view_GLES20Canvas_getMatrix },
     { "nConcatMatrix",      "(II)V",           (void*) android_view_GLES20Canvas_concatMatrix },
 
-    { "nDrawBitmap",        "(IIFFI)V",        (void*) android_view_GLES20Canvas_drawBitmap },
-    { "nDrawBitmap",        "(IIFFFFFFFFI)V",  (void*) android_view_GLES20Canvas_drawBitmapRect },
-    { "nDrawBitmap",        "(IIII)V",         (void*) android_view_GLES20Canvas_drawBitmapMatrix },
-    { "nDrawPatch",         "(II[BFFFFI)V",    (void*) android_view_GLES20Canvas_drawPatch },
+    { "nDrawBitmap",        "(II[BFFI)V", (void*) android_view_GLES20Canvas_drawBitmap },
+    { "nDrawBitmap",        "(II[BFFFFFFFFI)V", (void*) android_view_GLES20Canvas_drawBitmapRect },
+    { "nDrawBitmap",        "(II[BII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix },
+    { "nDrawPatch",         "(II[B[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch },
+
     { "nDrawColor",         "(III)V",          (void*) android_view_GLES20Canvas_drawColor },
     { "nDrawRect",          "(IFFFFI)V",       (void*) android_view_GLES20Canvas_drawRect },
     { "nDrawRects",         "(III)V",          (void*) android_view_GLES20Canvas_drawRects },
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index d283dea3..a8efd00 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -19,18 +19,15 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.DisplayMetrics;
-import android.util.Finalizers;
 
 import java.io.OutputStream;
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
 import java.nio.ShortBuffer;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
 
 public final class Bitmap implements Parcelable {
+
     /**
      * Indicates that the bitmap was created for an unknown pixel density.
      *
@@ -47,6 +44,17 @@
      */
     public final int mNativeBitmap;
 
+    /**
+     * Backing buffer for the Bitmap.
+     * Made public for quick access from drawing methods -- do NOT modify
+     * from outside this class.
+     *
+     * @hide
+     */
+    public byte[] mBuffer;
+
+    private final BitmapFinalizer mFinalizer;
+
     private final boolean mIsMutable;
     private byte[] mNinePatchChunk;   // may be null
     private int mWidth = -1;
@@ -85,25 +93,22 @@
 
         This can be called from JNI code.
     */
-    private Bitmap(int nativeBitmap, boolean isMutable, byte[] ninePatchChunk, int density) {
+    private Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
+            int density) {
         if (nativeBitmap == 0) {
             throw new RuntimeException("internal error: native bitmap is 0");
         }
 
+        mBuffer = buffer;
         // we delete this in our finalizer
         mNativeBitmap = nativeBitmap;
+        mFinalizer = new BitmapFinalizer(nativeBitmap);
+
         mIsMutable = isMutable;
         mNinePatchChunk = ninePatchChunk;
         if (density >= 0) {
             mDensity = density;
         }
-
-        // If the finalizers queue is null, we are running in zygote and the
-        // bitmap will never be reclaimed, so we don't need to run our native
-        // destructor
-        if (Finalizers.getQueue() != null) {
-            new BitmapFinalizer(this);
-        }
     }
 
     /**
@@ -169,6 +174,7 @@
      */
     public void recycle() {
         if (!mRecycled) {
+            mBuffer = null;
             nativeRecycle(mNativeBitmap);
             mNinePatchChunk = null;
             mRecycled = true;
@@ -520,6 +526,9 @@
      * @throws IllegalArgumentException if the width or height are <= 0
      */
     private static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) {
+        if (width <= 0 || height <= 0) {
+            throw new IllegalArgumentException("width and height must be > 0");
+        }
         Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true);
         if (config == Config.ARGB_8888 && !hasAlpha) {
             bm.eraseColor(0xff000000);
@@ -561,6 +570,9 @@
                 (lastScanline + width > length)) {
             throw new ArrayIndexOutOfBoundsException();
         }
+        if (width <= 0 || height <= 0) {
+            throw new IllegalArgumentException("width and height must be > 0");
+        }
         return nativeCreate(colors, offset, stride, width, height,
                             config.nativeInt, false);
     }
@@ -1059,22 +1071,16 @@
         nativePrepareToDraw(mNativeBitmap);
     }
 
-    private static class BitmapFinalizer extends Finalizers.ReclaimableReference<Bitmap> {
-        private static final Set<BitmapFinalizer> sFinalizers = Collections.synchronizedSet(
-                new HashSet<BitmapFinalizer>());
+    private static class BitmapFinalizer {
+        private final int mNativeBitmap;
 
-        private int mNativeBitmap;
-
-        BitmapFinalizer(Bitmap b) {
-            super(b, Finalizers.getQueue());
-            mNativeBitmap = b.mNativeBitmap;
-            sFinalizers.add(this);
+        BitmapFinalizer(int nativeBitmap) {
+            mNativeBitmap = nativeBitmap;
         }
 
         @Override
-        public void reclaim() {
+        public void finalize() {
             nativeDestructor(mNativeBitmap);
-            sFinalizers.remove(this);
         }
     }
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 07fe66c..66c6d81 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -74,6 +74,23 @@
      */
     public static final int DIRECTION_RTL = 1;
     
+    private final CanvasFinalizer mFinalizer;
+
+    private static class CanvasFinalizer {
+        private final int mNativeCanvas;
+
+        public CanvasFinalizer(int nativeCanvas) {
+            mNativeCanvas = nativeCanvas;
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            if (mNativeCanvas != 0) {
+                finalizer(mNativeCanvas);
+            }
+        }
+    }
+
     /**
      * Construct an empty raster canvas. Use setBitmap() to specify a bitmap to
      * draw into.  The initial target density is {@link Bitmap#DENSITY_NONE};
@@ -83,6 +100,7 @@
     public Canvas() {
         // 0 means no native bitmap
         mNativeCanvas = initRaster(0);
+        mFinalizer = new CanvasFinalizer(0);
     }
 
     /**
@@ -101,6 +119,7 @@
         }
         throwIfRecycled(bitmap);
         mNativeCanvas = initRaster(bitmap.ni());
+        mFinalizer = new CanvasFinalizer(mNativeCanvas);
         mBitmap = bitmap;
         mDensity = bitmap.mDensity;
     }
@@ -110,6 +129,7 @@
             throw new IllegalStateException();
         }
         mNativeCanvas = nativeCanvas;
+        mFinalizer = new CanvasFinalizer(nativeCanvas);
         mDensity = Bitmap.getDefaultDensity();
     }
 
@@ -1607,19 +1627,6 @@
     public void releaseContext() {
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            super.finalize();
-        } finally {
-            // If the constructor threw an exception before setting mNativeCanvas,
-            // the native finalizer must not be invoked.
-            if (mNativeCanvas != 0) {
-                finalizer(mNativeCanvas);
-            }
-        }
-    }
-
     /**
      * Free up as much memory as possible from private caches (e.g. fonts, images)
      *
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 00de39b..1c93ea6 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -60,7 +60,9 @@
 }
 
 void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) {
-    bitmapResource->pixelRef()->safeRef();
+    SkPixelRef* pixref = bitmapResource->pixelRef();
+    if (pixref) pixref->globalRef();
+
     bitmapResource->getColorTable()->safeRef();
     incrementRefcount((void*)bitmapResource, kBitmap);
 }
@@ -89,7 +91,9 @@
 }
 
 void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) {
-    bitmapResource->pixelRef()->safeUnref();
+    SkPixelRef* pixref = bitmapResource->pixelRef();
+    if (pixref) pixref->globalUnref();
+
     bitmapResource->getColorTable()->safeUnref();
     decrementRefcount((void*)bitmapResource);
 }