Pack preloaded framework assets in a texture atlas

When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.

This CL introduces an asset server that provides an atlas to all processes.

Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.

WHAT IS THE ASSETS ATLAS

The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)

HOW IS THE ASSETS ATLAS GENERATED

Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.

There are several steps that lead to the atlas generation:

1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas

2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.

3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.

HOW PROCESSES USE THE ATLAS

Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.

It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)

Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.

Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 42f4faf..c7976c3 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -16,8 +16,6 @@
 
 package android.content.res;
 
-import android.os.Trace;
-import android.view.View;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -30,6 +28,7 @@
 import android.graphics.drawable.Drawable.ConstantState;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Trace;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -1985,6 +1984,13 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    public LongSparseArray<Drawable.ConstantState> getPreloadedDrawables() {
+        return sPreloadedDrawables[0];
+    }
+
     private boolean verifyPreloadConfig(int changingConfigurations, int allowVarying,
             int resourceId, String name) {
         // We allow preloading of resources even if they vary by font scale (which
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 2ec9a7d..21a03d8 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -21,6 +21,7 @@
 import android.graphics.ColorFilter;
 import android.graphics.DrawFilter;
 import android.graphics.Matrix;
+import android.graphics.NinePatch;
 import android.graphics.Paint;
 import android.graphics.PaintFlagsDrawFilter;
 import android.graphics.Path;
@@ -314,21 +315,21 @@
      * 
      * @see #flushCaches(int) 
      */
-    public static final int FLUSH_CACHES_LAYERS = 0;
+    static final int FLUSH_CACHES_LAYERS = 0;
     
     /**
      * Must match Caches::FlushMode values
      * 
      * @see #flushCaches(int) 
      */
-    public static final int FLUSH_CACHES_MODERATE = 1;
+    static final int FLUSH_CACHES_MODERATE = 1;
 
     /**
      * Must match Caches::FlushMode values
      * 
      * @see #flushCaches(int) 
      */
-    public static final int FLUSH_CACHES_FULL = 2;
+    static final int FLUSH_CACHES_FULL = 2;
 
     /**
      * Flush caches to reclaim as much memory as possible. The amount of memory
@@ -338,10 +339,8 @@
      * {@link #FLUSH_CACHES_FULL}.
      * 
      * @param level Hint about the amount of memory to reclaim
-     * 
-     * @hide
      */
-    public static void flushCaches(int level) {
+    static void flushCaches(int level) {
         nFlushCaches(level);
     }
 
@@ -353,21 +352,28 @@
      * 
      * @hide
      */
-    public static void terminateCaches() {
+    static void terminateCaches() {
         nTerminateCaches();
     }
 
     private static native void nTerminateCaches();
 
-    /**
-     * @hide
-     */
-    public static void initCaches() {
-        nInitCaches();
+    static boolean initCaches() {
+        return nInitCaches();
     }
 
-    private static native void nInitCaches();
-    
+    private static native boolean nInitCaches();
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Atlas
+    ///////////////////////////////////////////////////////////////////////////
+
+    static void initAtlas(GraphicBuffer buffer, int[] map) {
+        nInitAtlas(buffer, map, map.length);
+    }
+
+    private static native void nInitAtlas(GraphicBuffer buffer, int[] map, int count);
+
     ///////////////////////////////////////////////////////////////////////////
     // Display list
     ///////////////////////////////////////////////////////////////////////////
@@ -718,20 +724,21 @@
     }
 
     @Override
-    public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
+    public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
+        Bitmap bitmap = patch.getBitmap();
         if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
         // Shaders are ignored when drawing patches
         int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
         try {
             final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-            nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks,
+            nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mChunk,
                     dst.left, dst.top, dst.right, dst.bottom, nativePaint);
         } finally {
             if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
         }
     }
 
-    private static native void nDrawPatch(int renderer, int bitmap, byte[] buffer, byte[] chunks,
+    private static native void nDrawPatch(int renderer, int bitmap, byte[] chunks,
             float left, float top, float right, float bottom, int paint);
 
     @Override
@@ -741,14 +748,14 @@
         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
         try {
             final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
+            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint);
         } finally {
             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
         }
     }
 
-    private static native void nDrawBitmap(
-            int renderer, int bitmap, byte[] buffer, float left, float top, int paint);
+    private static native void nDrawBitmap(int renderer, int bitmap,
+            float left, float top, int paint);
 
     @Override
     public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
@@ -757,15 +764,13 @@
         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
         try {
             final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
-                    matrix.native_instance, nativePaint);
+            nDrawBitmap(mRenderer, bitmap.mNativeBitmap,  matrix.native_instance, nativePaint);
         } finally {
             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
         }
     }
 
-    private static native void nDrawBitmap(int renderer, int bitmap, byte[] buff,
-            int matrix, int paint);
+    private static native void nDrawBitmap(int renderer, int bitmap, int matrix, int paint);
 
     @Override
     public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
@@ -787,7 +792,7 @@
                 bottom = src.bottom;
             }
 
-            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
                     dst.left, dst.top, dst.right, dst.bottom, nativePaint);
         } finally {
             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
@@ -814,14 +819,14 @@
                 bottom = src.bottom;
             }
     
-            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
+            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
                     dst.left, dst.top, dst.right, dst.bottom, nativePaint);
         } finally {
             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
         }
     }
 
-    private static native void nDrawBitmap(int renderer, int bitmap, byte[] buffer,
+    private static native void nDrawBitmap(int renderer, int bitmap,
             float srcLeft, float srcTop, float srcRight, float srcBottom,
             float left, float top, float right, float bottom, int paint);
 
@@ -891,14 +896,14 @@
         int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
         try {
             final int nativePaint = paint == null ? 0 : paint.mNativePaint;        
-            nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
+            nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, meshWidth, meshHeight,
                     verts, vertOffset, colors, colorOffset, nativePaint);
         } finally {
             if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
         }
     }
 
-    private static native void nDrawBitmapMesh(int renderer, int bitmap, byte[] buffer,
+    private static native void nDrawBitmapMesh(int renderer, int bitmap,
             int meshWidth, int meshHeight, float[] verts, int vertOffset,
             int[] colors, int colorOffset, int paint);
 
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index 3272504..d367267 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Matrix;
+import android.graphics.NinePatch;
 
 import java.util.ArrayList;
 
@@ -29,7 +30,8 @@
     // alive as long as the DisplayList is alive.  The Bitmap and DisplayList lists
     // are populated by the GLES20RecordingCanvas during appropriate drawing calls and are
     // cleared at the start of a new drawing frame or when the view is detached from the window.
-    final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(5);
+    final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(10);
+    final ArrayList<NinePatch> mNinePatches = new ArrayList<NinePatch>(10);
     final ArrayList<DisplayList> mChildDisplayLists = new ArrayList<DisplayList>();
 
     private GLES20RecordingCanvas mCanvas;
@@ -83,7 +85,12 @@
         }
         mValid = false;
 
+        clearReferences();
+    }
+
+    void clearReferences() {
         mBitmaps.clear();
+        mNinePatches.clear();
         mChildDisplayLists.clear();
     }
 
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index 7da2451..ec059d5 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -19,6 +19,7 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
 import android.graphics.Matrix;
+import android.graphics.NinePatch;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.Rect;
@@ -62,8 +63,7 @@
     }
 
     void start() {
-        mDisplayList.mBitmaps.clear();
-        mDisplayList.mChildDisplayLists.clear();
+        mDisplayList.clearReferences();
     }
 
     int end(int nativeDisplayList) {
@@ -80,9 +80,10 @@
     }
 
     @Override
-    public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
-        super.drawPatch(bitmap, chunks, dst, paint);
-        mDisplayList.mBitmaps.add(bitmap);
+    public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
+        super.drawPatch(patch, dst, paint);
+        mDisplayList.mBitmaps.add(patch.getBitmap());
+        mDisplayList.mNinePatches.add(patch);
         // Shaders in the Paint are ignored when drawing a Bitmap
     }
 
diff --git a/core/java/android/view/GraphicBuffer.aidl b/core/java/android/view/GraphicBuffer.aidl
new file mode 100644
index 0000000..6dc6bed
--- /dev/null
+++ b/core/java/android/view/GraphicBuffer.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+parcelable GraphicBuffer;
diff --git a/core/java/android/view/GraphicBuffer.java b/core/java/android/view/GraphicBuffer.java
new file mode 100644
index 0000000..b4576f3
--- /dev/null
+++ b/core/java/android/view/GraphicBuffer.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Simple wrapper for the native GraphicBuffer class.
+ *
+ * @hide
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class GraphicBuffer implements Parcelable {
+    // Note: keep usage flags in sync with GraphicBuffer.h and gralloc.h
+    public static final int USAGE_SW_READ_NEVER = 0x0;
+    public static final int USAGE_SW_READ_RARELY = 0x2;
+    public static final int USAGE_SW_READ_OFTEN = 0x3;
+    public static final int USAGE_SW_READ_MASK = 0xF;
+
+    public static final int USAGE_SW_WRITE_NEVER = 0x0;
+    public static final int USAGE_SW_WRITE_RARELY = 0x20;
+    public static final int USAGE_SW_WRITE_OFTEN = 0x30;
+    public static final int USAGE_SW_WRITE_MASK = 0xF0;
+
+    public static final int USAGE_SOFTWARE_MASK = USAGE_SW_READ_MASK | USAGE_SW_WRITE_MASK;
+
+    public static final int USAGE_PROTECTED = 0x4000;
+
+    public static final int USAGE_HW_TEXTURE = 0x100;
+    public static final int USAGE_HW_RENDER = 0x200;
+    public static final int USAGE_HW_2D = 0x400;
+    public static final int USAGE_HW_COMPOSER = 0x800;
+    public static final int USAGE_HW_VIDEO_ENCODER = 0x10000;
+    public static final int USAGE_HW_MASK = 0x71F00;
+
+    private final int mWidth;
+    private final int mHeight;
+    private final int mFormat;
+    private final int mUsage;
+    // Note: do not rename, this field is used by native code
+    private final int mNativeObject;
+
+    // These two fields are only used by lock/unlockCanvas()
+    private Canvas mCanvas;
+    private int mSaveCount;
+
+    /**
+     * Creates new <code>GraphicBuffer</code> instance. This method will return null
+     * if the buffer cannot be created.
+     *
+     * @param width The width in pixels of the buffer
+     * @param height The height in pixels of the buffer
+     * @param format The format of each pixel as specified in {@link PixelFormat}
+     * @param usage Hint indicating how the buffer will be used
+     *
+     * @return A <code>GraphicBuffer</code> instance or null
+     */
+    public static GraphicBuffer create(int width, int height, int format, int usage) {
+        int nativeObject = nCreateGraphicBuffer(width, height, format, usage);
+        if (nativeObject != 0) {
+            return new GraphicBuffer(width, height, format, usage, nativeObject);
+        }
+        return null;
+    }
+
+    /**
+     * Private use only. See {@link #create(int, int, int, int)}.
+     */
+    private GraphicBuffer(int width, int height, int format, int usage, int nativeObject) {
+        mWidth = width;
+        mHeight = height;
+        mFormat = format;
+        mUsage = usage;
+        mNativeObject = nativeObject;
+    }
+
+    /**
+     * Returns the width of this buffer in pixels.
+     */
+    public int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * Returns the height of this buffer in pixels.
+     */
+    public int getHeight() {
+        return mHeight;
+    }
+
+    /**
+     * Returns the pixel format of this buffer. The pixel format must be one of
+     * the formats defined in {@link PixelFormat}.
+     */
+    public int getFormat() {
+        return mFormat;
+    }
+
+    /**
+     * Returns the usage hint set on this buffer.
+     */
+    public int getUsage() {
+        return mUsage;
+    }
+
+    /**
+     * <p>Start editing the pixels in the buffer. A null is returned if the buffer
+     * cannot be locked for editing.</p>
+     *
+     * <p>The content of the buffer is preserved between unlockCanvas()
+     * and lockCanvas().</p>
+     *
+     * @return A Canvas used to draw into the buffer, or null.
+     *
+     * @see #lockCanvas(android.graphics.Rect)
+     * @see #unlockCanvasAndPost(android.graphics.Canvas)
+     */
+    public Canvas lockCanvas() {
+        return lockCanvas(null);
+    }
+
+    /**
+     * Just like {@link #lockCanvas()} but allows specification of a dirty
+     * rectangle.
+     *
+     * @param dirty Area of the buffer that may be modified.
+
+     * @return A Canvas used to draw into the surface or null
+     *
+     * @see #lockCanvas()
+     * @see #unlockCanvasAndPost(android.graphics.Canvas)
+     */
+    public Canvas lockCanvas(Rect dirty) {
+        if (mCanvas == null) {
+            mCanvas = new Canvas();
+        }
+
+        if (nLockCanvas(mNativeObject, mCanvas, dirty)) {
+            mSaveCount = mCanvas.save();
+            return mCanvas;
+        }
+
+        return null;
+    }
+
+    /**
+     * Finish editing pixels in the buffer.
+     *
+     * @param canvas The Canvas previously returned by lockCanvas()
+     *
+     * @see #lockCanvas()
+     * @see #lockCanvas(android.graphics.Rect)
+     */
+    public void unlockCanvasAndPost(Canvas canvas) {
+        if (mCanvas != null && canvas == mCanvas) {
+            canvas.restoreToCount(mSaveCount);
+            mSaveCount = 0;
+
+            nUnlockCanvasAndPost(mNativeObject, mCanvas);
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            nDestroyGraphicBuffer(mNativeObject);
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mWidth);
+        dest.writeInt(mHeight);
+        dest.writeInt(mFormat);
+        dest.writeInt(mUsage);
+        nWriteGraphicBufferToParcel(mNativeObject, dest);
+    }
+
+    public static final Parcelable.Creator<GraphicBuffer> CREATOR =
+            new Parcelable.Creator<GraphicBuffer>() {
+        public GraphicBuffer createFromParcel(Parcel in) {
+            int width = in.readInt();
+            int height = in.readInt();
+            int format = in.readInt();
+            int usage = in.readInt();
+            int nativeObject = nReadGraphicBufferFromParcel(in);
+            if (nativeObject != 0) {
+                return new GraphicBuffer(width, height, format, usage, nativeObject);
+            }
+            return null;
+        }
+
+        public GraphicBuffer[] newArray(int size) {
+            return new GraphicBuffer[size];
+        }
+    };
+
+    private static native int nCreateGraphicBuffer(int width, int height, int format, int usage);
+    private static native void nDestroyGraphicBuffer(int nativeObject);
+    private static native void nWriteGraphicBufferToParcel(int nativeObject, Parcel dest);
+    private static native int nReadGraphicBufferFromParcel(Parcel in);
+    private static native boolean nLockCanvas(int nativeObject, Canvas canvas, Rect dirty);
+    private static native boolean nUnlockCanvasAndPost(int nativeObject, Canvas canvas);
+}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 8308459..0632e1d 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -24,7 +24,10 @@
 import android.opengl.GLUtils;
 import android.opengl.ManagedEGLContext;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
@@ -968,7 +971,7 @@
             if (fallback) {
                 // we'll try again if it was context lost
                 setRequested(false);
-                Log.w(LOG_TAG, "Mountain View, we've had a problem here. " 
+                Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
                         + "Switching back to software rendering.");
             }
         }
@@ -976,7 +979,7 @@
         @Override
         boolean initialize(Surface surface) throws Surface.OutOfResourcesException {
             if (isRequested() && !isEnabled()) {
-                initializeEgl();
+                boolean contextCreated = initializeEgl();
                 mGl = createEglSurface(surface);
                 mDestroyed = false;
 
@@ -991,6 +994,10 @@
                             mCanvas.setName(mName);
                         }
                         setEnabled(true);
+
+                        if (contextCreated) {
+                            initAtlas();
+                        }
                     }
 
                     return mCanvas != null;
@@ -1010,7 +1017,7 @@
 
         abstract int[] getConfig(boolean dirtyRegions);
 
-        void initializeEgl() {
+        boolean initializeEgl() {
             synchronized (sEglLock) {
                 if (sEgl == null && sEglConfig == null) {
                     sEgl = (EGL10) EGLContext.getEGL();
@@ -1043,7 +1050,10 @@
             if (mEglContext == null) {
                 mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
                 sEglContextStorage.set(createManagedContext(mEglContext));
+                return true;
             }
+
+            return false;
         }
 
         private EGLConfig loadEglConfig() {
@@ -1181,6 +1191,7 @@
         }
 
         abstract void initCaches();
+        abstract void initAtlas();
 
         EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
             int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE };
@@ -1193,6 +1204,7 @@
                         "Could not create an EGL context. eglCreateContext failed with error: " +
                         GLUtils.getEGLErrorString(sEgl.eglGetError()));
             }
+
             return context;
         }
 
@@ -1788,7 +1800,27 @@
 
         @Override
         void initCaches() {
-            GLES20Canvas.initCaches();
+            if (GLES20Canvas.initCaches()) {
+                // Caches were (re)initialized, rebind atlas
+                initAtlas();
+            }
+        }
+
+        @Override
+        void initAtlas() {
+            IBinder binder = ServiceManager.getService("assetatlas");
+            IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
+            try {
+                GraphicBuffer buffer = atlas.getBuffer();
+                if (buffer != null) {
+                    int[] map = atlas.getMap();
+                    if (map != null) {
+                        GLES20Canvas.initAtlas(buffer, map);
+                    }
+                }
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, "Could not acquire atlas", e);
+            }
         }
 
         @Override
diff --git a/core/java/android/view/IAssetAtlas.aidl b/core/java/android/view/IAssetAtlas.aidl
new file mode 100644
index 0000000..2595179
--- /dev/null
+++ b/core/java/android/view/IAssetAtlas.aidl
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.view.GraphicBuffer;
+
+/**
+ * Programming interface to the system assets atlas. This atlas, when
+ * present, holds preloaded drawable in a single, shareable graphics
+ * buffer. This allows multiple processes to share the same data to
+ * save up on memory.
+ *
+ * @hide
+ */
+interface IAssetAtlas {
+    /**
+     * Returns the atlas buffer (texture) or null if the atlas is
+     * not available yet.
+     */
+    GraphicBuffer getBuffer();
+
+    /**
+     * Returns the map of the bitmaps stored in the atlas or null
+     * if the atlas is not available yet.
+     *
+     * Each bitmap is represented by several entries in the array:
+     * int0: SkBitmap*, the native bitmap object
+     * int1: x position
+     * int2: y position
+     * int3: rotated, 1 if the bitmap must be rotated, 0 otherwise
+     */
+    int[] getMap();
+}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f34d390..47c40d2 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -23,7 +23,6 @@
 import android.content.ComponentCallbacks;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -108,8 +107,6 @@
     private static final boolean DEBUG_FPS = false;
     private static final boolean DEBUG_INPUT_PROCESSING = false || LOCAL_LOGV;
 
-    private static final boolean USE_RENDER_THREAD = false;
-
     /**
      * Set this system property to true to force the view hierarchy to render
      * at 60 Hz. This can be used to measure the potential framerate.
@@ -131,10 +128,6 @@
     static final ArrayList<ComponentCallbacks> sConfigCallbacks
             = new ArrayList<ComponentCallbacks>();
 
-    private static boolean sUseRenderThread = false;
-    private static boolean sRenderThreadQueried = false;
-    private static final Object[] sRenderThreadQueryLock = new Object[0];
-
     final Context mContext;
     final IWindowSession mWindowSession;
     final Display mDisplay;
@@ -375,35 +368,6 @@
         loadSystemProperties();
     }
 
-    /**
-     * @return True if the application requests the use of a separate render thread,
-     *         false otherwise
-     */
-    private static boolean isRenderThreadRequested(Context context) {
-        if (USE_RENDER_THREAD) {
-            synchronized (sRenderThreadQueryLock) {
-                if (!sRenderThreadQueried) {
-                    final PackageManager packageManager = context.getPackageManager();
-                    final String packageName = context.getApplicationInfo().packageName;
-                    try {
-                        ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName,
-                                PackageManager.GET_META_DATA);
-                        if (applicationInfo.metaData != null) {
-                            sUseRenderThread = applicationInfo.metaData.getBoolean(
-                                    "android.graphics.renderThread", false);
-                        }
-                    } catch (PackageManager.NameNotFoundException e) {
-                    } finally {
-                        sRenderThreadQueried = true;
-                    }
-                }
-                return sUseRenderThread;
-            }
-        } else {
-            return false;
-        }
-    }
-
     public static void addFirstDrawHandler(Runnable callback) {
         synchronized (sFirstDrawHandlers) {
             if (!sFirstDrawComplete) {
@@ -481,7 +445,7 @@
 
                 // If the application owns the surface, don't enable hardware acceleration
                 if (mSurfaceHolder == null) {
-                    enableHardwareAcceleration(mView.getContext(), attrs);
+                    enableHardwareAcceleration(attrs);
                 }
 
                 boolean restore = false;
@@ -689,7 +653,7 @@
         }
     }
 
-    private void enableHardwareAcceleration(Context context, WindowManager.LayoutParams attrs) {
+    private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
         mAttachInfo.mHardwareAccelerated = false;
         mAttachInfo.mHardwareAccelerationRequested = false;
 
@@ -729,11 +693,6 @@
                     return;
                 }
 
-                final boolean renderThread = isRenderThreadRequested(context);
-                if (renderThread) {
-                    Log.i(HardwareRenderer.LOG_TAG, "Render threat initiated");
-                }
-
                 if (mAttachInfo.mHardwareRenderer != null) {
                     mAttachInfo.mHardwareRenderer.destroy(true);
                 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index edc0baf..d0d3508 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -50,6 +50,7 @@
 	android_view_KeyEvent.cpp \
 	android_view_KeyCharacterMap.cpp \
 	android_view_HardwareRenderer.cpp \
+	android_view_GraphicBuffer.cpp \
 	android_view_GLES20DisplayList.cpp \
 	android_view_GLES20Canvas.cpp \
 	android_view_MotionEvent.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 53fde48..e243ed7 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -116,6 +116,7 @@
 extern int register_android_graphics_Xfermode(JNIEnv* env);
 extern int register_android_graphics_PixelFormat(JNIEnv* env);
 extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
+extern int register_android_view_GraphicBuffer(JNIEnv* env);
 extern int register_android_view_GLES20DisplayList(JNIEnv* env);
 extern int register_android_view_GLES20Canvas(JNIEnv* env);
 extern int register_android_view_HardwareRenderer(JNIEnv* env);
@@ -1111,6 +1112,7 @@
     REG_JNI(register_android_nio_utils),
     REG_JNI(register_android_graphics_PixelFormat),
     REG_JNI(register_android_graphics_Graphics),
+    REG_JNI(register_android_view_GraphicBuffer),
     REG_JNI(register_android_view_GLES20DisplayList),
     REG_JNI(register_android_view_GLES20Canvas),
     REG_JNI(register_android_view_HardwareRenderer),
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index b87fe27..c8fa290 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -16,18 +16,19 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
-#include <EGL/egl.h>
-
 #include "jni.h"
 #include "GraphicsJNI.h"
 #include <nativehelper/JNIHelp.h>
 
+#include "android_view_GraphicBuffer.h"
+
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
-#include <gui/GLConsumer.h>
 
 #include <androidfw/ResourceTypes.h>
 
+#include <gui/GLConsumer.h>
+
 #include <private/hwui/DrawGlInfo.h>
 
 #include <cutils/properties.h>
@@ -99,10 +100,11 @@
     }
 }
 
-static void android_view_GLES20Canvas_initCaches(JNIEnv* env, jobject clazz) {
+static bool android_view_GLES20Canvas_initCaches(JNIEnv* env, jobject clazz) {
     if (Caches::hasInstance()) {
-        Caches::getInstance().init();
+        return Caches::getInstance().init();
     }
+    return false;
 }
 
 static void android_view_GLES20Canvas_terminateCaches(JNIEnv* env, jobject clazz) {
@@ -112,6 +114,21 @@
 }
 
 // ----------------------------------------------------------------------------
+// Caching
+// ----------------------------------------------------------------------------
+
+static void android_view_GLES20Canvas_initAtlas(JNIEnv* env, jobject clazz,
+        jobject graphicBuffer, jintArray atlasMapArray, jint count) {
+
+    sp<GraphicBuffer> buffer = graphicBufferForJavaObject(env, graphicBuffer);
+    jint* atlasMap = env->GetIntArrayElements(atlasMapArray, NULL);
+
+    Caches::getInstance().assetAtlas.init(buffer, atlasMap, count);
+
+    env->ReleaseIntArrayElements(atlasMapArray, atlasMap, 0);
+}
+
+// ----------------------------------------------------------------------------
 // Constructors
 // ----------------------------------------------------------------------------
 
@@ -350,31 +367,20 @@
 // ----------------------------------------------------------------------------
 
 static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject clazz,
-        OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer,
-        jfloat left, jfloat top, SkPaint* paint) {
-    // This object allows the renderer to allocate a global JNI ref to the buffer object.
-    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
-
+        OpenGLRenderer* renderer, SkBitmap* bitmap, jfloat left, jfloat top, SkPaint* paint) {
     renderer->drawBitmap(bitmap, left, top, paint);
 }
 
 static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject clazz,
-        OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer,
+        OpenGLRenderer* renderer, SkBitmap* bitmap,
         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 clazz,
-        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);
-
+        OpenGLRenderer* renderer, SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
     renderer->drawBitmap(bitmap, matrix, paint);
 }
 
@@ -404,12 +410,8 @@
 }
 
 static void android_view_GLES20Canvas_drawBitmapMesh(JNIEnv* env, jobject clazz,
-        OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer,
-        jint meshWidth, jint meshHeight, jfloatArray vertices, jint offset,
-        jintArray colors, jint colorOffset, SkPaint* paint) {
-    // This object allows the renderer to allocate a global JNI ref to the buffer object.
-    JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
-
+        OpenGLRenderer* renderer, SkBitmap* bitmap, jint meshWidth, jint meshHeight,
+         jfloatArray vertices, jint offset, jintArray colors, jint colorOffset, SkPaint* paint) {
     jfloat* verticesArray = vertices ? env->GetFloatArrayElements(vertices, NULL) + offset : NULL;
     jint* colorsArray = colors ? env->GetIntArrayElements(colors, NULL) + colorOffset : NULL;
 
@@ -420,18 +422,13 @@
 }
 
 static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject clazz,
-        OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray buffer, jbyteArray chunks,
+        OpenGLRenderer* renderer, SkBitmap* bitmap, 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,
-            left, top, right, bottom, paint);
+    renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint);
 
     env->ReleaseByteArrayElements(chunks, storage, 0);
 }
@@ -932,9 +929,12 @@
 
 #ifdef USE_OPENGL_RENDERER
     { "nFlushCaches",       "(I)V",            (void*) android_view_GLES20Canvas_flushCaches },
-    { "nInitCaches",        "()V",             (void*) android_view_GLES20Canvas_initCaches },
+    { "nInitCaches",        "()Z",             (void*) android_view_GLES20Canvas_initCaches },
     { "nTerminateCaches",   "()V",             (void*) android_view_GLES20Canvas_terminateCaches },
 
+    { "nInitAtlas",         "(Landroid/view/GraphicBuffer;[II)V",
+            (void*) android_view_GLES20Canvas_initAtlas },
+
     { "nCreateRenderer",    "()I",             (void*) android_view_GLES20Canvas_createRenderer },
     { "nDestroyRenderer",   "(I)V",            (void*) android_view_GLES20Canvas_destroyRenderer },
     { "nSetViewport",       "(III)V",          (void*) android_view_GLES20Canvas_setViewport },
@@ -977,14 +977,14 @@
     { "nGetMatrix",         "(II)V",           (void*) android_view_GLES20Canvas_getMatrix },
     { "nConcatMatrix",      "(II)V",           (void*) android_view_GLES20Canvas_concatMatrix },
 
-    { "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 },
+    { "nDrawBitmap",        "(IIFFI)V",        (void*) android_view_GLES20Canvas_drawBitmap },
+    { "nDrawBitmap",        "(IIFFFFFFFFI)V",  (void*) android_view_GLES20Canvas_drawBitmapRect },
+    { "nDrawBitmap",        "(IIII)V",         (void*) android_view_GLES20Canvas_drawBitmapMatrix },
     { "nDrawBitmap",        "(I[IIIFFIIZI)V",  (void*) android_view_GLES20Canvas_drawBitmapData },
 
-    { "nDrawBitmapMesh",    "(II[BII[FI[III)V",(void*) android_view_GLES20Canvas_drawBitmapMesh },
+    { "nDrawBitmapMesh",    "(IIII[FI[III)V",  (void*) android_view_GLES20Canvas_drawBitmapMesh },
 
-    { "nDrawPatch",         "(II[B[BFFFFI)V",  (void*) android_view_GLES20Canvas_drawPatch },
+    { "nDrawPatch",         "(II[BFFFFI)V",    (void*) android_view_GLES20Canvas_drawPatch },
 
     { "nDrawColor",         "(III)V",          (void*) android_view_GLES20Canvas_drawColor },
     { "nDrawRect",          "(IFFFFI)V",       (void*) android_view_GLES20Canvas_drawRect },
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
new file mode 100644
index 0000000..d68c0b2
--- /dev/null
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GraphicBuffer"
+
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include "android_os_Parcel.h"
+#include "android_view_GraphicBuffer.h"
+
+#include <android_runtime/AndroidRuntime.h>
+
+#include <binder/Parcel.h>
+
+#include <ui/GraphicBuffer.h>
+#include <ui/PixelFormat.h>
+
+#include <gui/IGraphicBufferAlloc.h>
+#include <gui/ISurfaceComposer.h>
+
+#include <SkCanvas.h>
+#include <SkBitmap.h>
+
+#include <private/gui/ComposerService.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+// Defines
+// ----------------------------------------------------------------------------
+
+// Debug
+#define DEBUG_GRAPHIC_BUFFER 0
+
+// Debug
+#if DEBUG_GRAPHIC_BUFFER
+    #define GB_LOGD(...) ALOGD(__VA_ARGS__)
+    #define GB_LOGW(...) ALOGW(__VA_ARGS__)
+#else
+    #define GB_LOGD(...)
+    #define GB_LOGW(...)
+#endif
+
+#define LOCK_CANVAS_USAGE GraphicBuffer::USAGE_SW_READ_OFTEN | GraphicBuffer::USAGE_SW_WRITE_OFTEN
+
+// ----------------------------------------------------------------------------
+// JNI Helpers
+// ----------------------------------------------------------------------------
+
+static struct {
+    jfieldID mNativeObject;
+} gGraphicBufferClassInfo;
+
+static struct {
+    jmethodID set;
+    jfieldID left;
+    jfieldID top;
+    jfieldID right;
+    jfieldID bottom;
+} gRectClassInfo;
+
+static struct {
+    jfieldID mFinalizer;
+    jfieldID mNativeCanvas;
+    jfieldID mSurfaceFormat;
+} gCanvasClassInfo;
+
+static struct {
+    jfieldID mNativeCanvas;
+} gCanvasFinalizerClassInfo;
+
+#define GET_INT(object, field) \
+    env->GetIntField(object, field)
+
+#define SET_INT(object, field, value) \
+    env->SetIntField(object, field, value)
+
+#define INVOKEV(object, method, ...) \
+    env->CallVoidMethod(object, method, __VA_ARGS__)
+
+// ----------------------------------------------------------------------------
+// Types
+// ----------------------------------------------------------------------------
+
+class GraphicBufferWrapper {
+public:
+    GraphicBufferWrapper(const sp<GraphicBuffer>& buffer): buffer(buffer) {
+    }
+
+    sp<GraphicBuffer> buffer;
+};
+
+// ----------------------------------------------------------------------------
+// GraphicBuffer lifecycle
+// ----------------------------------------------------------------------------
+
+static GraphicBufferWrapper* android_view_GraphiceBuffer_create(JNIEnv* env, jobject clazz,
+        jint width, jint height, jint format, jint usage) {
+
+    sp<ISurfaceComposer> composer(ComposerService::getComposerService());
+    sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc());
+    if (alloc == NULL) {
+        GB_LOGW("createGraphicBufferAlloc() failed in GraphicBuffer.create()");
+        return NULL;
+    }
+
+    status_t error;
+    sp<GraphicBuffer> buffer(alloc->createGraphicBuffer(width, height, format, usage, &error));
+    if (buffer == NULL) {
+        GB_LOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
+        return NULL;
+    }
+
+    return new GraphicBufferWrapper(buffer);
+}
+
+static void android_view_GraphiceBuffer_destroy(JNIEnv* env, jobject clazz,
+        GraphicBufferWrapper* wrapper) {
+    delete wrapper;
+}
+
+// ----------------------------------------------------------------------------
+// Canvas management
+// ----------------------------------------------------------------------------
+
+static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
+    jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
+    SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
+            GET_INT(canvasObj, gCanvasClassInfo.mNativeCanvas));
+    SET_INT(canvasObj, gCanvasClassInfo.mNativeCanvas, (int) newCanvas);
+    SET_INT(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (int) newCanvas);
+    SkSafeUnref(previousCanvas);
+}
+
+static inline SkBitmap::Config convertPixelFormat(int32_t format) {
+    switch (format) {
+        case PIXEL_FORMAT_RGBA_8888:
+            return SkBitmap::kARGB_8888_Config;
+        case PIXEL_FORMAT_RGBX_8888:
+            return SkBitmap::kARGB_8888_Config;
+        case PIXEL_FORMAT_RGB_565:
+            return SkBitmap::kRGB_565_Config;
+        default:
+            return SkBitmap::kNo_Config;
+    }
+}
+
+static jboolean android_view_GraphicBuffer_lockCanvas(JNIEnv* env, jobject,
+        GraphicBufferWrapper* wrapper, jobject canvas, jobject dirtyRect) {
+
+    if (!wrapper) {
+        return false;
+    }
+
+    sp<GraphicBuffer> buffer(wrapper->buffer);
+
+    Rect rect;
+    if (dirtyRect) {
+        rect.left = GET_INT(dirtyRect, gRectClassInfo.left);
+        rect.top = GET_INT(dirtyRect, gRectClassInfo.top);
+        rect.right = GET_INT(dirtyRect, gRectClassInfo.right);
+        rect.bottom = GET_INT(dirtyRect, gRectClassInfo.bottom);
+    } else {
+        rect.set(Rect(buffer->getWidth(), buffer->getHeight()));
+    }
+
+    void* bits = NULL;
+    status_t status = buffer->lock(LOCK_CANVAS_USAGE, rect, &bits);
+
+    if (status) return false;
+    if (!bits) {
+        buffer->unlock();
+        return false;
+    }
+
+    ssize_t bytesCount = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
+
+    SkBitmap bitmap;
+    bitmap.setConfig(convertPixelFormat(buffer->getPixelFormat()),
+            buffer->getWidth(), buffer->getHeight(), bytesCount);
+
+    if (buffer->getWidth() > 0 && buffer->getHeight() > 0) {
+        bitmap.setPixels(bits);
+    } else {
+        bitmap.setPixels(NULL);
+    }
+
+    SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer->getPixelFormat());
+
+    SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap));
+    swapCanvasPtr(env, canvas, nativeCanvas);
+
+    SkRect clipRect;
+    clipRect.set(rect.left, rect.top, rect.right, rect.bottom);
+    nativeCanvas->clipRect(clipRect);
+
+    if (dirtyRect) {
+        INVOKEV(dirtyRect, gRectClassInfo.set,
+                int(rect.left), int(rect.top), int(rect.right), int(rect.bottom));
+    }
+
+    return true;
+}
+
+static jboolean android_view_GraphicBuffer_unlockCanvasAndPost(JNIEnv* env, jobject,
+        GraphicBufferWrapper* wrapper, jobject canvas) {
+
+    SkCanvas* nativeCanvas = SkNEW(SkCanvas);
+    swapCanvasPtr(env, canvas, nativeCanvas);
+
+    if (wrapper) {
+        status_t status = wrapper->buffer->unlock();
+        return status == 0;
+    }
+
+    return false;
+}
+
+// ----------------------------------------------------------------------------
+// Serialization
+// ----------------------------------------------------------------------------
+
+static void android_view_GraphiceBuffer_write(JNIEnv* env, jobject clazz,
+        GraphicBufferWrapper* wrapper, jobject dest) {
+    Parcel* parcel = parcelForJavaObject(env, dest);
+    if (parcel) {
+        parcel->write(*wrapper->buffer);
+    }
+}
+
+static GraphicBufferWrapper* android_view_GraphiceBuffer_read(JNIEnv* env, jobject clazz,
+        jobject in) {
+
+    Parcel* parcel = parcelForJavaObject(env, in);
+    if (parcel) {
+        sp<GraphicBuffer> buffer = new GraphicBuffer();
+        parcel->read(*buffer);
+        return new GraphicBufferWrapper(buffer);
+    }
+
+    return NULL;
+}
+
+// ----------------------------------------------------------------------------
+// External helpers
+// ----------------------------------------------------------------------------
+
+sp<GraphicBuffer> graphicBufferForJavaObject(JNIEnv* env, jobject obj) {
+    if (obj) {
+        jint nativeObject = env->GetIntField(obj, gGraphicBufferClassInfo.mNativeObject);
+        GraphicBufferWrapper* wrapper = (GraphicBufferWrapper*) nativeObject;
+        if (wrapper != NULL) {
+            sp<GraphicBuffer> buffer(wrapper->buffer);
+            return buffer;
+        }
+    }
+    return NULL;
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+#define FIND_CLASS(var, className) \
+        var = env->FindClass(className); \
+        LOG_FATAL_IF(! var, "Unable to find class " className);
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+        var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+        var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+        LOG_FATAL_IF(!var, "Unable to find method " methodName);
+
+const char* const kClassPathName = "android/view/GraphicBuffer";
+
+static JNINativeMethod gMethods[] = {
+    { "nCreateGraphicBuffer",  "(IIII)I", (void*) android_view_GraphiceBuffer_create },
+    { "nDestroyGraphicBuffer", "(I)V",    (void*) android_view_GraphiceBuffer_destroy },
+
+    { "nWriteGraphicBufferToParcel",  "(ILandroid/os/Parcel;)V",
+            (void*) android_view_GraphiceBuffer_write },
+    { "nReadGraphicBufferFromParcel", "(Landroid/os/Parcel;)I",
+            (void*) android_view_GraphiceBuffer_read },
+
+    { "nLockCanvas", "(ILandroid/graphics/Canvas;Landroid/graphics/Rect;)Z",
+            (void*) android_view_GraphicBuffer_lockCanvas },
+    { "nUnlockCanvasAndPost", "(ILandroid/graphics/Canvas;)Z",
+            (void*) android_view_GraphicBuffer_unlockCanvasAndPost },
+};
+
+int register_android_view_GraphicBuffer(JNIEnv* env) {
+    jclass clazz;
+    FIND_CLASS(clazz, "android/view/GraphicBuffer");
+    GET_FIELD_ID(gGraphicBufferClassInfo.mNativeObject, clazz, "mNativeObject", "I");
+
+    FIND_CLASS(clazz, "android/graphics/Rect");
+    GET_METHOD_ID(gRectClassInfo.set, clazz, "set", "(IIII)V");
+    GET_FIELD_ID(gRectClassInfo.left, clazz, "left", "I");
+    GET_FIELD_ID(gRectClassInfo.top, clazz, "top", "I");
+    GET_FIELD_ID(gRectClassInfo.right, clazz, "right", "I");
+    GET_FIELD_ID(gRectClassInfo.bottom, clazz, "bottom", "I");
+
+    FIND_CLASS(clazz, "android/graphics/Canvas");
+    GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer",
+            "Landroid/graphics/Canvas$CanvasFinalizer;");
+    GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "I");
+    GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I");
+
+    FIND_CLASS(clazz, "android/graphics/Canvas$CanvasFinalizer");
+    GET_FIELD_ID(gCanvasFinalizerClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "I");
+
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+};
diff --git a/core/jni/android_view_GraphicBuffer.h b/core/jni/android_view_GraphicBuffer.h
new file mode 100644
index 0000000..509587c
--- /dev/null
+++ b/core/jni/android_view_GraphicBuffer.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/GraphicBuffer.h>
+
+#include "jni.h"
+
+namespace android {
+
+// This function does not perform any type checking, the specified
+// object must be an instance of android.view.GraphicBuffer
+extern sp<GraphicBuffer> graphicBufferForJavaObject(JNIEnv* env, jobject obj);
+
+}
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index a1985bc..d515696 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -126,12 +126,12 @@
 }
 
 static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) {
-  jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
-  SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
+    jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer);
+    SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>(
           env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas));
-  env->SetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas, (int)newCanvas);
-  env->SetIntField(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (int)newCanvas);
-  SkSafeUnref(previousCanvas);
+    env->SetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas, (int)newCanvas);
+    env->SetIntField(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (int)newCanvas);
+    SkSafeUnref(previousCanvas);
 }
 
 static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject,