Merge "Introduce SystemUI-managed alternative system bars."
diff --git a/api/current.txt b/api/current.txt
index a896ecb..481521e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18385,6 +18385,40 @@
 
 }
 
+package android.print.pdf {
+
+  public final class PdfDocument {
+    method public void close();
+    method protected final void finalize() throws java.lang.Throwable;
+    method public void finishPage(android.print.pdf.PdfDocument.Page);
+    method public java.util.List<android.print.pdf.PdfDocument.PageInfo> getPages();
+    method public static android.print.pdf.PdfDocument open();
+    method public android.print.pdf.PdfDocument.Page startPage(android.print.pdf.PdfDocument.PageInfo);
+    method public void writeTo(java.io.OutputStream);
+  }
+
+  public static final class PdfDocument.Page {
+    method public android.graphics.Canvas getCanvas();
+    method public android.print.pdf.PdfDocument.PageInfo getInfo();
+  }
+
+  public static final class PdfDocument.PageInfo {
+    method public android.graphics.Rect getContentSize();
+    method public int getDesity();
+    method public android.graphics.Matrix getInitialTransform();
+    method public int getPageNumber();
+    method public android.graphics.Rect getPageSize();
+  }
+
+  public static final class PdfDocument.PageInfo.Builder {
+    ctor public PdfDocument.PageInfo.Builder(android.graphics.Rect, int, int);
+    method public android.print.pdf.PdfDocument.PageInfo create();
+    method public android.print.pdf.PdfDocument.PageInfo.Builder setContentSize(android.graphics.Rect);
+    method public android.print.pdf.PdfDocument.PageInfo.Builder setInitialTransform(android.graphics.Matrix);
+  }
+
+}
+
 package android.provider {
 
   public final class AlarmClock {
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index b3f0d96..f090e07 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -72,7 +72,13 @@
      */
     @Deprecated
     public static final String SYNC_EXTRAS_ACCOUNT = "account";
+
+    /**
+     * If this extra is set to true, the sync request will be scheduled
+     * at the front of the sync request queue and without any delay
+     */
     public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
+
     /**
      * @deprecated instead use
      * {@link #SYNC_EXTRAS_MANUAL}
@@ -104,8 +110,25 @@
      */
     public static final String SYNC_EXTRAS_MANUAL = "force";
 
+    /**
+     * Indicates that this sync is intended to only upload local changes to the server.
+     * For example, this will be set to true if the sync is initiated by a call to
+     * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
+     */
     public static final String SYNC_EXTRAS_UPLOAD = "upload";
+
+    /**
+     * Indicates that the sync adapter should proceed with the delete operations,
+     * even if it determines that there are too many.
+     * See {@link SyncResult#tooManyDeletions}
+     */
     public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
+
+    /**
+     * Indicates that the sync adapter should not proceed with the delete operations,
+     * if it determines that there are too many.
+     * See {@link SyncResult#tooManyDeletions}
+     */
     public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions";
 
     /**
diff --git a/core/java/android/print/pdf/PdfDocument.java b/core/java/android/print/pdf/PdfDocument.java
new file mode 100644
index 0000000..7fb170e
--- /dev/null
+++ b/core/java/android/print/pdf/PdfDocument.java
@@ -0,0 +1,443 @@
+/*
+ * 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.print.pdf;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+
+import dalvik.system.CloseGuard;
+
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * <p>
+ * This class enables generating a PDF document from native Android content. You
+ * open a new document and then for every page you want to add you start a page,
+ * write content to the page, and finish the page. After you are done with all
+ * pages, you write the document to an output stream and close the document.
+ * After a document is closed you should not use it anymore.
+ * </p>
+ * <p>
+ * A typical use of the APIs looks like this:
+ * </p>
+ * <pre>
+ * // open a new document
+ * PdfDocument document = PdfDocument.open();
+ *
+ * // crate a page description
+ * PageInfo pageInfo = new PageInfo.Builder(new Rect(0, 0, 100, 100), 1, 300).create();
+ *
+ * // start a page
+ * Page page = document.startPage(pageInfo);
+ *
+ * // draw something on the page
+ * View content = getContentView();
+ * content.draw(page.getCanvas());
+ *
+ * // finish the page
+ * document.finishPage(page);
+ * . . .
+ * add more pages
+ * . . .
+ * // write the document content
+ * document.writeTo(getOutputStream());
+ *
+ * //close the document
+ * document.close();
+ * </pre>
+ */
+public final class PdfDocument {
+
+    private final byte[] mChunk = new byte[4096];
+
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+
+    private final List<PageInfo> mPages = new ArrayList<PageInfo>();
+
+    private int mNativeDocument;
+
+    private Page mCurrentPage;
+
+    /**
+     * Opens a new document.
+     * <p>
+     * <strong>Note:</strong> You must close the document after you are
+     * done by calling {@link #close()}
+     * </p>
+     *
+     * @return The document.
+     *
+     * @see #close()
+     */
+    public static PdfDocument open() {
+        return new PdfDocument();
+    }
+
+    /**
+     * Creates a new instance.
+     */
+    private PdfDocument() {
+        mNativeDocument = nativeCreateDocument();
+        mCloseGuard.open("close");
+    }
+
+    /**
+     * Starts a page using the provided {@link PageInfo}. After the page
+     * is created you can draw arbitrary content on the page's canvas which
+     * you can get by calling {@link Page#getCanvas()}. After you are done
+     * drawing the content you should finish the page by calling
+     * {@link #finishPage(Page). After the page is finished you should
+     * no longer access the page or its canvas.
+     * <p>
+     * <strong>Note:</strong> Do not call this method after {@link #close()}.
+     * </p>
+     *
+     * @param pageInfo The page info.
+     * @return A blank page.
+     *
+     * @see #finishPage(Page)
+     */
+    public Page startPage(PageInfo pageInfo) {
+        throwIfClosed();
+        if (pageInfo == null) {
+            throw new IllegalArgumentException("page cannot be null!");
+        }
+        if (mCurrentPage != null) {
+            throw new IllegalStateException("Previous page not finished!");
+        }
+        Canvas canvas = new PdfCanvas(nativeCreatePage(pageInfo.mPageSize,
+                pageInfo.mContentSize, pageInfo.mInitialTransform.native_instance),
+                pageInfo.mDensity);
+        mCurrentPage = new Page(canvas, pageInfo);
+        return mCurrentPage;
+    }
+
+    /**
+     * Finishes a started page. You should always finish the last started page.
+     * <p>
+     * <strong>Note:</strong> Do not call this method after {@link #close()}.
+     * </p>
+     *
+     * @param page The page.
+     *
+     * @see #startPage(PageInfo)
+     */
+    public void finishPage(Page page) {
+        throwIfClosed();
+        if (page == null) {
+            throw new IllegalArgumentException("page cannot be null");
+        }
+        if (page != mCurrentPage) {
+            throw new IllegalStateException("invalid page");
+        }
+        mPages.add(page.getInfo());
+        mCurrentPage = null;
+        nativeAppendPage(mNativeDocument, page.mCanvas.mNativeCanvas);
+        page.finish();
+    }
+
+    /**
+     * Writes the document to an output stream.
+     * <p>
+     * <strong>Note:</strong> Do not call this method after {@link #close()}.
+     * </p>
+     *
+     * @param out The output stream.
+     */
+    public void writeTo(OutputStream out) {
+        throwIfClosed();
+        if (out == null) {
+            throw new IllegalArgumentException("out cannot be null!");
+        }
+        nativeWriteTo(mNativeDocument, out, mChunk);
+    }
+
+    /**
+     * Gets the pages of the document.
+     *
+     * @return The pages.
+     */
+    public List<PageInfo> getPages() {
+        return Collections.unmodifiableList(mPages);
+    }
+
+    /**
+     * Closes this document. This method should be called after you
+     * are done working with the document. After this call the document
+     * is considered closed and none of its methods should be called.
+     */
+    public void close() {
+        dispose();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            mCloseGuard.warnIfOpen();
+            dispose();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private void dispose() {
+        if (mNativeDocument != 0) {
+            nativeFinalize(mNativeDocument);
+            mCloseGuard.close();
+            mNativeDocument = 0;
+        }
+    }
+
+    /**
+     * Throws an exception if the document is already closed.
+     */
+    private void throwIfClosed() {
+        if (mNativeDocument == 0) {
+            throw new IllegalStateException("document is closed!");
+        }
+    }
+
+    private native int nativeCreateDocument();
+
+    private native void nativeFinalize(int document);
+
+    private native void nativeAppendPage(int document, int page);
+
+    private native void nativeWriteTo(int document, OutputStream out, byte[] chunk);
+
+    private static native int nativeCreatePage(Rect pageSize,
+            Rect contentSize, int nativeMatrix);
+
+
+    private final class PdfCanvas extends Canvas {
+
+        public PdfCanvas(int nativeCanvas, int density) {
+            super(nativeCanvas);
+            super.setDensity(density);
+        }
+
+        @Override
+        public void setBitmap(Bitmap bitmap) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setDensity(int density) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void setScreenDensity(int density) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /**
+     * This class represents meta-data that describes a PDF {@link Page}.
+     */
+    public static final class PageInfo {
+        private Rect mPageSize;
+        private Rect mContentSize;
+        private Matrix mInitialTransform;
+        private int mPageNumber;
+        private int mDensity;
+
+        /**
+         * Creates a new instance.
+         */
+        private PageInfo() {
+            /* do nothing */
+        }
+
+        /**
+         * Gets the page size in pixels.
+         *
+         * @return The page size.
+         */
+        public Rect getPageSize() {
+            return mPageSize;
+        }
+
+        /**
+         * Get the content size in pixels.
+         *
+         * @return The content size.
+         */
+        public Rect getContentSize() {
+            return mContentSize;
+        }
+
+        /**
+         * Gets the initial transform which is applied to the page. This may be
+         * useful to move the origin to account for a margin, apply scale, or
+         * apply a rotation.
+         *
+         * @return The initial transform.
+         */
+        public Matrix getInitialTransform() {
+            return mInitialTransform;
+        }
+
+        /**
+         * Gets the page number.
+         *
+         * @return The page number.
+         */
+        public int getPageNumber() {
+            return mPageNumber;
+        }
+
+        /**
+         * Gets the density of the page in DPI.
+         *
+         * @return The density.
+         */
+        public int getDesity() {
+            return mDensity;
+        }
+
+        /**
+         * Builder for creating a {@link PageInfo}.
+         */
+        public static final class Builder {
+            private final PageInfo mPageInfo = new PageInfo();
+
+            /**
+             * Creates a new builder with the mandatory page info attributes.
+             *
+             * @param pageSize The page size in pixels.
+             * @param pageNumber The page number.
+             * @param density The page density in DPI.
+             */
+            public Builder(Rect pageSize, int pageNumber, int density) {
+                if (pageSize.width() == 0 || pageSize.height() == 0) {
+                    throw new IllegalArgumentException("page width and height" +
+                            " must be greater than zero!");
+                }
+                if (pageNumber < 0) {
+                    throw new IllegalArgumentException("pageNumber cannot be less than zero!");
+                }
+                if (density <= 0) {
+                    throw new IllegalArgumentException("density must be greater than zero!");
+                }
+                mPageInfo.mPageSize = pageSize;
+                mPageInfo.mPageNumber = pageNumber;
+                mPageInfo.mDensity = density;
+            }
+
+            /**
+             * Sets the content size in pixels.
+             *
+             * @param contentSize The content size.
+             */
+            public Builder setContentSize(Rect contentSize) {
+                Rect pageSize = mPageInfo.mPageSize;
+                if (contentSize != null && (pageSize.left > contentSize.left
+                        || pageSize.top > contentSize.top
+                        || pageSize.right < contentSize.right
+                        || pageSize.bottom < contentSize.bottom)) {
+                    throw new IllegalArgumentException("contentSize does not fit the pageSize!");
+                }
+                mPageInfo.mContentSize = contentSize;
+                return this;
+            }
+
+            /**
+             * Sets the initial transform which is applied to the page. This may be
+             * useful to move the origin to account for a margin, apply scale, or
+             * apply a rotation.
+             *
+             * @param transform The initial transform.
+             */
+            public Builder setInitialTransform(Matrix transform) {
+                mPageInfo.mInitialTransform = transform;
+                return this;
+            }
+
+            /**
+             * Creates a new {@link PageInfo}.
+             *
+             * @return The new instance.
+             */
+            public PageInfo create() {
+                if (mPageInfo.mContentSize == null) {
+                    mPageInfo.mContentSize = mPageInfo.mPageSize;
+                }
+                if (mPageInfo.mInitialTransform == null) {
+                    mPageInfo.mInitialTransform = new Matrix();
+                }
+                return mPageInfo;
+            }
+        }
+    }
+
+    /**
+     * This class represents a PDF document page. It has associated
+     * a canvas on which you can draw content and is acquired by a
+     * call to {@link #getCanvas()}. It also has associated a
+     * {@link PageInfo} instance that describes its attributes.
+     */
+    public static final class Page {
+        private final PageInfo mPageInfo;
+        private Canvas mCanvas;
+
+        /**
+         * Creates a new instance.
+         *
+         * @param canvas The canvas of the page.
+         * @param pageInfo The info with meta-data.
+         */
+        private Page(Canvas canvas, PageInfo pageInfo) {
+            mCanvas = canvas;
+            mPageInfo = pageInfo;
+        }
+
+        /**
+         * Gets the {@link Canvas} of the page.
+         *
+         * @return The canvas if the page is not finished, null otherwise.
+         *
+         * @see PdfDocument#finishPage(Page)
+         */
+        public Canvas getCanvas() {
+            return mCanvas;
+        }
+
+        /**
+         * Gets the {@link PageInfo} with meta-data for the page.
+         *
+         * @return The page info.
+         *
+         * @see PdfDocument#finishPage(Page)
+         */
+        public PageInfo getInfo() {
+            return mPageInfo;
+        }
+
+        private void finish() {
+            if (mCanvas != null) {
+                mCanvas.release();
+                mCanvas = null;
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e62abbe..9bc66be 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -923,9 +923,14 @@
     }
 
     void disposeResizeBuffer() {
-        if (mResizeBuffer != null) {
-            mResizeBuffer.destroy();
-            mResizeBuffer = null;
+        if (mResizeBuffer != null && mAttachInfo.mHardwareRenderer != null) {
+            mAttachInfo.mHardwareRenderer.safelyRun(new Runnable() {
+                @Override
+                public void run() {
+                    mResizeBuffer.destroy();
+                    mResizeBuffer = null;
+                }
+            });
         }
     }
 
@@ -1466,8 +1471,7 @@
                             if (mResizeBuffer != null) {
                                 mResizeBuffer.end(hwRendererCanvas);
                                 if (!completed) {
-                                    mResizeBuffer.destroy();
-                                    mResizeBuffer = null;
+                                    disposeResizeBuffer();
                                 }
                             }
                         }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 5e1090f..faaf588 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -140,6 +140,7 @@
 	android_util_FileObserver.cpp \
 	android/opengl/poly_clip.cpp.arm \
 	android/opengl/util.cpp.arm \
+	android/print/android_print_pdf_PdfDocument.cpp \
 	android_server_NetworkManagementSocketTagger.cpp \
 	android_server_Watchdog.cpp \
 	android_ddm_DdmHandleNativeHeap.cpp \
@@ -164,6 +165,7 @@
 	$(call include-path-for, libhardware_legacy)/hardware_legacy \
 	$(TOP)/frameworks/av/include \
 	external/skia/src/core \
+	external/skia/src/pdf \
 	external/skia/src/images \
 	external/skia/include/utils \
 	external/sqlite/dist \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 0158bf5..144cc84 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -144,6 +144,7 @@
 extern int register_android_os_FileUtils(JNIEnv *env);
 extern int register_android_os_UEventObserver(JNIEnv* env);
 extern int register_android_os_MemoryFile(JNIEnv* env);
+extern int register_android_print_pdf_PdfDocument(JNIEnv* env);
 extern int register_android_net_LocalSocketImpl(JNIEnv* env);
 extern int register_android_net_NetworkUtils(JNIEnv* env);
 extern int register_android_net_TrafficStats(JNIEnv* env);
@@ -1182,6 +1183,7 @@
     REG_JNI(register_android_os_SELinux),
     REG_JNI(register_android_os_Trace),
     REG_JNI(register_android_os_UEventObserver),
+    REG_JNI(register_android_print_pdf_PdfDocument),
     REG_JNI(register_android_net_LocalSocketImpl),
     REG_JNI(register_android_net_NetworkUtils),
     REG_JNI(register_android_net_TrafficStats),
diff --git a/core/jni/android/print/android_print_pdf_PdfDocument.cpp b/core/jni/android/print/android_print_pdf_PdfDocument.cpp
new file mode 100644
index 0000000..745fe8a
--- /dev/null
+++ b/core/jni/android/print/android_print_pdf_PdfDocument.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "jni.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "SkCanvas.h"
+#include "SkPDFDevice.h"
+#include "SkPDFDocument.h"
+#include "SkRect.h"
+#include "SkSize.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+namespace android {
+
+static jint nativeCreateDocument(JNIEnv* env, jobject clazz) {
+    return reinterpret_cast<jint>(new SkPDFDocument());
+}
+
+static void nativeFinalize(JNIEnv* env, jobject thiz, jint documentPtr) {
+    delete reinterpret_cast<SkPDFDocument*>(documentPtr);
+}
+
+static jint nativeCreatePage(JNIEnv* env, jobject thiz,
+        jobject pageSize, jobject contentSize, jint initialTransformation) {
+    SkIRect skPageSizeRect;
+    GraphicsJNI::jrect_to_irect(env, pageSize, &skPageSizeRect);
+    SkISize skPageSize = SkISize::Make(skPageSizeRect.width(),
+            skPageSizeRect.height());
+
+    SkIRect skContentRect;
+    GraphicsJNI::jrect_to_irect(env, contentSize, &skContentRect);
+    SkISize skContentSize = SkISize::Make(skContentRect.width(),
+            skContentRect.height());
+
+    SkMatrix* transformation = reinterpret_cast<SkMatrix*>(initialTransformation);
+    SkPDFDevice* skPdfDevice = new SkPDFDevice(skPageSize, skContentSize, *transformation);
+
+    return reinterpret_cast<jint>(new SkCanvas(skPdfDevice));
+}
+
+static void nativeAppendPage(JNIEnv* env, jobject thiz, jint documentPtr, jint pagePtr) {
+    SkCanvas* page = reinterpret_cast<SkCanvas*>(pagePtr);
+    SkPDFDocument* document = reinterpret_cast<SkPDFDocument*>(documentPtr);
+    SkPDFDevice* device = static_cast<SkPDFDevice*>(page->getDevice());
+    document->appendPage(device);
+}
+
+static void nativeWriteTo(JNIEnv* env, jobject clazz, jint documentPtr,
+        jobject out, jbyteArray chunk) {
+    SkWStream* skWStream = CreateJavaOutputStreamAdaptor(env, out, chunk);
+    SkPDFDocument* document = reinterpret_cast<SkPDFDocument*>(documentPtr);
+    document->emitPDF(skWStream);
+    delete skWStream;
+}
+
+static JNINativeMethod gPdfDocument_Methods[] = {
+    {"nativeCreateDocument", "()I", (void*) nativeCreateDocument},
+    {"nativeFinalize", "(I)V", (void*) nativeFinalize},
+    {"nativeCreatePage", "(Landroid/graphics/Rect;Landroid/graphics/Rect;I)I",
+            (void*) nativeCreatePage},
+    {"nativeAppendPage", "(II)V", (void*) nativeAppendPage},
+    {"nativeWriteTo", "(ILjava/io/OutputStream;[B)V", (void*) nativeWriteTo}
+};
+
+int register_android_print_pdf_PdfDocument(JNIEnv* env) {
+    int result = android::AndroidRuntime::registerNativeMethods(
+            env, "android/print/pdf/PdfDocument", gPdfDocument_Methods,
+            NELEM(gPdfDocument_Methods));
+    return result;
+}
+
+};
diff --git a/docs/html/distribute/googleplay/edu/about.jd b/docs/html/distribute/googleplay/edu/about.jd
index 7306c5b..cc131c64 100644
--- a/docs/html/distribute/googleplay/edu/about.jd
+++ b/docs/html/distribute/googleplay/edu/about.jd
@@ -24,6 +24,7 @@
 that will help schools. We want to help you create innovative educational apps,
 without having to knock on school doors to reach teachers and students.</p>
 
+<p><a class="landing-page-link" style="text-align:right;" href="#video">Watch a Video</a></p>
 
 <div class="landing-docs">
   <div class="col-6 normal-links">
@@ -79,3 +80,25 @@
 
 
 </div>
+<div id="video" style="background: #F0F0F0;
+            border-top: 1px solid #DDD;
+            padding: 0px 0 24px 0;
+            overflow: auto;
+            clear:both;
+            margin-bottom:40px;
+            margin-top:30px;">
+   <div style="padding:0 0 0 29px;">
+        <h4>Introducing Google Play for Education</h4>
+
+          <div style="width:700px;">
+          <p style="margin-top:26px;
+                    margin-bottom:12px;">
+          Hear how Google Play for Education works and how developers can leverage the unique business opportunities in creating educational apps for the K-12 market. There's a demo at 4m10s.</p>
+           </div>
+           <iframe style="float:left;
+             margin-right:24px;
+             margin-top:14px;" width="700" height="394" src=
+             "http://www.youtube.com/embed/haEmsMo0f3w?HD=1;rel=0;origin=developer.android.com;" frameborder="0" allowfullscreen>
+           </iframe>
+   </div> 
+</div>
diff --git a/docs/html/distribute/googleplay/promote/badges.jd b/docs/html/distribute/googleplay/promote/badges.jd
index 738e76b..9a32921 100644
--- a/docs/html/distribute/googleplay/promote/badges.jd
+++ b/docs/html/distribute/googleplay/promote/badges.jd
@@ -201,10 +201,8 @@
           onchange="changeBadgeLang()">
     <option title="Afrikaans"
             value="af">Afrikaans</option>
-<!--
     <option title="Arabic"
             value="ar">العربية</option>
--->
     <option title="Belarusian"
             value="be">Беларуская</option>
     <option title="Bulgarian"
diff --git a/docs/html/images/brand/ar_generic_rgb_wo_45.png b/docs/html/images/brand/ar_generic_rgb_wo_45.png
index bdb0ff6..c403608 100644
--- a/docs/html/images/brand/ar_generic_rgb_wo_45.png
+++ b/docs/html/images/brand/ar_generic_rgb_wo_45.png
Binary files differ
diff --git a/docs/html/images/brand/ar_generic_rgb_wo_60.png b/docs/html/images/brand/ar_generic_rgb_wo_60.png
index ba9fc69..95e31eb 100644
--- a/docs/html/images/brand/ar_generic_rgb_wo_60.png
+++ b/docs/html/images/brand/ar_generic_rgb_wo_60.png
Binary files differ
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index a1c87cb..bd871c4 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -37,12 +37,14 @@
  * Canvas and Drawables</a> developer guide.</p></div>
  */
 public class Canvas {
+
     // assigned in constructors or setBitmap, freed in finalizer
-    int mNativeCanvas;
-    
+    /** @hide */
+    public int mNativeCanvas;
+
     // may be null
     private Bitmap mBitmap;
-    
+
     // optional field set by the caller
     private DrawFilter mDrawFilter;
 
@@ -59,7 +61,6 @@
     protected int mScreenDensity = Bitmap.DENSITY_NONE;
     
     // Used by native code
-    @SuppressWarnings({"UnusedDeclaration"})
     private int mSurfaceFormat;
 
     /**
@@ -79,10 +80,9 @@
     private static final int MAXMIMUM_BITMAP_SIZE = 32766;
 
     // This field is used to finalize the native Canvas properly
-    @SuppressWarnings({"UnusedDeclaration"})
     private final CanvasFinalizer mFinalizer;
 
-    private static class CanvasFinalizer {
+    private static final class CanvasFinalizer {
         private int mNativeCanvas;
 
         public CanvasFinalizer(int nativeCanvas) {
@@ -92,13 +92,18 @@
         @Override
         protected void finalize() throws Throwable {
             try {
-                if (mNativeCanvas != 0) {
-                    finalizer(mNativeCanvas);
-                }
+                dispose();
             } finally {
                 super.finalize();
             }
         }
+
+        public void dispose() {
+            if (mNativeCanvas != 0) {
+                finalizer(mNativeCanvas);
+                mNativeCanvas = 0;
+            }
+        }
     }
 
     /**
@@ -132,13 +137,14 @@
         mBitmap = bitmap;
         mDensity = bitmap.mDensity;
     }
-    
-    Canvas(int nativeCanvas) {
+
+    /** @hide */
+    public Canvas(int nativeCanvas) {
         if (nativeCanvas == 0) {
             throw new IllegalStateException();
         }
         mNativeCanvas = nativeCanvas;
-        mFinalizer = new CanvasFinalizer(nativeCanvas);
+        mFinalizer = new CanvasFinalizer(mNativeCanvas);
         mDensity = Bitmap.getDefaultDensity();
     }
 
@@ -155,7 +161,18 @@
         }
         finalizer(oldCanvas);
     }
-    
+
+    /**
+     * Gets the native canvas pointer.
+     *
+     * @return The native pointer.
+     *
+     * @hide
+     */
+    public int getNativeCanvas() {
+        return mNativeCanvas;
+    }
+
     /**
      * Returns null.
      * 
@@ -1660,6 +1677,15 @@
     }
 
     /**
+     * Releases the resources associated with this canvas.
+     *
+     * @hide
+     */
+    public void release() {
+        mFinalizer.dispose();
+    }
+
+    /**
      * Free up as much memory as possible from private caches (e.g. fonts, images)
      *
      * @hide
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 887e822..7ce15c5 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -152,8 +152,9 @@
 
 class MergingDrawBatch : public DrawBatch {
 public:
-    MergingDrawBatch(DeferInfo& deferInfo, Rect viewport) :
-           DrawBatch(deferInfo), mClipRect(viewport), mClipSideFlags(kClipSide_None) {}
+    MergingDrawBatch(DeferInfo& deferInfo, int width, int height) :
+            DrawBatch(deferInfo), mClipRect(width, height),
+            mClipSideFlags(kClipSide_None) {}
 
     /*
      * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
@@ -489,6 +490,7 @@
     // complex clip has a complex set of expectations on the renderer state - for now, avoid taking
     // the merge path in those cases
     deferInfo.mergeable &= !recordingComplexClip();
+    deferInfo.opaqueOverBounds &= !recordingComplexClip() && mSaveStack.isEmpty();
 
     if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() &&
             op->state.mClipSideFlags != kClipSide_ConservativeFull &&
@@ -570,7 +572,8 @@
 
     if (!targetBatch) {
         if (deferInfo.mergeable) {
-            targetBatch = new MergingDrawBatch(deferInfo, mBounds);
+            targetBatch = new MergingDrawBatch(deferInfo,
+                    renderer.getViewportWidth(), renderer.getViewportHeight());
             mMergingBatches[deferInfo.batchId].put(deferInfo.mergeId, targetBatch);
         } else {
             targetBatch = new DrawBatch(deferInfo);
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index f678bfd..6099f5d 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -77,7 +77,7 @@
     virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
             bool useQuickReject) = 0;
 
-    virtual void output(int level, uint32_t logFlags = 0) = 0;
+    virtual void output(int level, uint32_t logFlags = 0) const = 0;
 
     // NOTE: it would be nice to declare constants and overriding the implementation in each op to
     // point at the constants, but that seems to require a .cpp file
@@ -278,7 +278,7 @@
         renderer.save(mFlags);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Save flags %x", mFlags);
     }
 
@@ -312,7 +312,7 @@
         renderer.restoreToCount(saveCount + mCount);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Restore to count %d", mCount);
     }
 
@@ -351,7 +351,7 @@
         renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom, mAlpha, mMode, mFlags);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("SaveLayer%s of area " RECT_STRING,
                 (isSaveLayerAlpha() ? "Alpha" : ""),RECT_ARGS(mArea));
     }
@@ -372,7 +372,7 @@
         return this;
     }
 
-    bool isSaveLayerAlpha() { return mAlpha < 255 && mMode == SkXfermode::kSrcOver_Mode; }
+    bool isSaveLayerAlpha() const { return mAlpha < 255 && mMode == SkXfermode::kSrcOver_Mode; }
     Rect mArea;
     int mAlpha;
     SkXfermode::Mode mMode;
@@ -388,7 +388,7 @@
         renderer.translate(mDx, mDy);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Translate by %f %f", mDx, mDy);
     }
 
@@ -408,7 +408,7 @@
         renderer.rotate(mDegrees);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Rotate by %f degrees", mDegrees);
     }
 
@@ -427,7 +427,7 @@
         renderer.scale(mSx, mSy);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Scale by %f %f", mSx, mSy);
     }
 
@@ -447,7 +447,7 @@
         renderer.skew(mSx, mSy);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Skew by %f %f", mSx, mSy);
     }
 
@@ -467,7 +467,7 @@
         renderer.setMatrix(mMatrix);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("SetMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
     }
 
@@ -486,7 +486,7 @@
         renderer.concatMatrix(mMatrix);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("ConcatMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
     }
 
@@ -530,7 +530,7 @@
         renderer.clipRect(mArea.left, mArea.top, mArea.right, mArea.bottom, mOp);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("ClipRect " RECT_STRING, RECT_ARGS(mArea));
     }
 
@@ -559,7 +559,7 @@
         renderer.clipPath(mPath, mOp);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         SkRect bounds = mPath->getBounds();
         OP_LOG("ClipPath bounds " RECT_STRING,
                 bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
@@ -580,7 +580,7 @@
         renderer.clipRegion(mRegion, mOp);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         SkIRect bounds = mRegion->getBounds();
         OP_LOG("ClipRegion bounds %d %d %d %d",
                 bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
@@ -599,7 +599,7 @@
         renderer.resetShader();
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOGS("ResetShader");
     }
 
@@ -614,7 +614,7 @@
         renderer.setupShader(mShader);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("SetupShader, shader %p", mShader);
     }
 
@@ -630,7 +630,7 @@
         renderer.resetColorFilter();
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOGS("ResetColorFilter");
     }
 
@@ -646,7 +646,7 @@
         renderer.setupColorFilter(mColorFilter);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("SetupColorFilter, filter %p", mColorFilter);
     }
 
@@ -662,7 +662,7 @@
         renderer.resetShadow();
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOGS("ResetShadow");
     }
 
@@ -678,7 +678,7 @@
         renderer.setupShadow(mRadius, mDx, mDy, mColor);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("SetupShadow, radius %f, %f, %f, color %#x", mRadius, mDx, mDy, mColor);
     }
 
@@ -697,7 +697,7 @@
         renderer.resetPaintFilter();
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOGS("ResetPaintFilter");
     }
 
@@ -713,7 +713,7 @@
         renderer.setupPaintFilter(mClearBits, mSetBits);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("SetupPaintFilter, clear %#x, set %#x", mClearBits, mSetBits);
     }
 
@@ -751,12 +751,18 @@
     TextureVertex::set(ptr++, posRect.xDim - offsetRect.left, posRect.yDim - offsetRect.top, \
             texCoordsRect.xDim, texCoordsRect.yDim)
 
+    /**
+     * This multi-draw operation builds a mesh on the stack by generating a quad
+     * for each bitmap in the batch. This method is also responsible for dirtying
+     * the current layer, if any.
+     */
     virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
             const Vector<DrawOp*>& ops, const Rect& bounds) {
         renderer.restoreDisplayState(state, true); // restore all but the clip
         TextureVertex vertices[6 * ops.size()];
         TextureVertex* vertex = &vertices[0];
 
+        const bool hasLayer = renderer.hasLayer();
         bool transformed = false;
 
         // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op,
@@ -778,13 +784,18 @@
             SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom);
             SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top);
             SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, bottom);
+
+            if (hasLayer) {
+                const Rect& dirty = ops[i]->state.mBounds;
+                renderer.dirtyLayer(dirty.left, dirty.top, dirty.right, dirty.bottom);
+            }
         }
 
         return renderer.drawBitmaps(mBitmap, ops.size(), &vertices[0],
                 transformed, bounds, mPaint);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw bitmap %p at %f %f", mBitmap, mLocalBounds.left, mLocalBounds.top);
     }
 
@@ -822,7 +833,7 @@
         return renderer.drawBitmap(mBitmap, mMatrix, getPaint(renderer));
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw bitmap %p matrix " MATRIX_STRING, mBitmap, MATRIX_ARGS(mMatrix));
     }
 
@@ -850,7 +861,7 @@
                 getPaint(renderer));
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING,
                 mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds));
     }
@@ -876,7 +887,7 @@
                 mLocalBounds.top, getPaint(renderer));
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw bitmap %p", mBitmap);
     }
 
@@ -900,7 +911,7 @@
                 mVertices, mColors, getPaint(renderer));
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw bitmap %p mesh %d x %d", mBitmap, mMeshWidth, mMeshHeight);
     }
 
@@ -921,28 +932,107 @@
 class DrawPatchOp : public DrawBoundedOp {
 public:
     DrawPatchOp(SkBitmap* bitmap, Res_png_9patch* patch,
-            float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode)
-            : DrawBoundedOp(left, top, right, bottom, 0),
-            mBitmap(bitmap), mPatch(patch), mAlpha(alpha), mMode(mode),
-            mGenerationId(0), mMesh(NULL) {
+            float left, float top, float right, float bottom, SkPaint* paint)
+            : DrawBoundedOp(left, top, right, bottom, paint),
+            mBitmap(bitmap), mPatch(patch), mGenerationId(0), mMesh(NULL) {
         mEntry = Caches::getInstance().assetAtlas.getEntry(bitmap);
     };
 
-    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
+    const Patch* getMesh(OpenGLRenderer& renderer) {
         if (!mMesh || renderer.getCaches().patchCache.getGenerationId() != mGenerationId) {
             PatchCache& cache = renderer.getCaches().patchCache;
             mMesh = cache.get(mEntry, mBitmap->width(), mBitmap->height(),
-                    mLocalBounds.right - mLocalBounds.left, mLocalBounds.bottom - mLocalBounds.top,
-                    mPatch);
+                    mLocalBounds.getWidth(), mLocalBounds.getHeight(), mPatch);
             mGenerationId = cache.getGenerationId();
         }
-        // We're not calling the public variant of drawPatch() here
-        // This method won't perform the quickReject() since we've already done it at this point
-        return renderer.drawPatch(mBitmap, mMesh, mEntry, mLocalBounds.left, mLocalBounds.top,
-                mLocalBounds.right, mLocalBounds.bottom, mAlpha, mMode);
+        return mMesh;
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    /**
+     * This multi-draw operation builds an indexed mesh on the stack by copying
+     * and transforming the vertices of each 9-patch in the batch. This method
+     * is also responsible for dirtying the current layer, if any.
+     */
+    virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
+            const Vector<DrawOp*>& ops, const Rect& bounds) {
+        renderer.restoreDisplayState(state, true);
+
+        // Batches will usually contain a small number of items so it's
+        // worth performing a first iteration to count the exact number
+        // of vertices we need in the new mesh
+        uint32_t totalVertices = 0;
+        for (unsigned int i = 0; i < ops.size(); i++) {
+            totalVertices += ((DrawPatchOp*) ops[i])->getMesh(renderer)->verticesCount;
+        }
+
+        const bool hasLayer = renderer.hasLayer();
+
+        uint32_t indexCount = 0;
+
+        TextureVertex vertices[totalVertices];
+        TextureVertex* vertex = &vertices[0];
+
+        // Create a mesh that contains the transformed vertices for all the
+        // 9-patch objects that are part of the batch. Note that onDefer()
+        // enforces ops drawn by this function to have a pure translate or
+        // identity matrix
+        for (unsigned int i = 0; i < ops.size(); i++) {
+            DrawPatchOp* patchOp = (DrawPatchOp*) ops[i];
+            const Patch* opMesh = patchOp->getMesh(renderer);
+            uint32_t vertexCount = opMesh->verticesCount;
+            if (vertexCount == 0) continue;
+
+            // We use the bounds to know where to translate our vertices
+            // Using patchOp->state.mBounds wouldn't work because these
+            // bounds are clipped
+            const float tx = (int) floorf(patchOp->state.mMatrix.getTranslateX() +
+                    patchOp->mLocalBounds.left + 0.5f);
+            const float ty = (int) floorf(patchOp->state.mMatrix.getTranslateY() +
+                    patchOp->mLocalBounds.top + 0.5f);
+
+            // Copy & transform all the vertices for the current operation
+            TextureVertex* opVertices = opMesh->vertices;
+            for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
+                TextureVertex::set(vertex++,
+                        opVertices->position[0] + tx, opVertices->position[1] + ty,
+                        opVertices->texture[0], opVertices->texture[1]);
+            }
+
+            // Dirty the current layer if possible. When the 9-patch does not
+            // contain empty quads we can take a shortcut and simply set the
+            // dirty rect to the object's bounds.
+            if (hasLayer) {
+                if (!opMesh->hasEmptyQuads) {
+                    renderer.dirtyLayer(tx, ty,
+                            tx + patchOp->mLocalBounds.getWidth(),
+                            ty + patchOp->mLocalBounds.getHeight());
+                } else {
+                    const size_t count = opMesh->quads.size();
+                    for (size_t i = 0; i < count; i++) {
+                        const Rect& quadBounds = opMesh->quads[i];
+                        const float x = tx + quadBounds.left;
+                        const float y = ty + quadBounds.top;
+                        renderer.dirtyLayer(x, y,
+                                x + quadBounds.getWidth(), y + quadBounds.getHeight());
+                    }
+                }
+            }
+
+            indexCount += opMesh->indexCount;
+        }
+
+        return renderer.drawPatches(mBitmap, mEntry, &vertices[0], indexCount, getPaint(renderer));
+    }
+
+    virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
+        // We're not calling the public variant of drawPatch() here
+        // This method won't perform the quickReject() since we've already done it at this point
+        return renderer.drawPatch(mBitmap, getMesh(renderer), mEntry,
+                mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom,
+                getPaint(renderer));
+    }
+
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds));
     }
 
@@ -951,7 +1041,8 @@
     virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {
         deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch;
         deferInfo.mergeId = mEntry ? (mergeid_t) &mEntry->atlas : (mergeid_t) mBitmap;
-        deferInfo.mergeable = true;
+        deferInfo.mergeable = state.mMatrix.isPureTranslate() &&
+                OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
         deferInfo.opaqueOverBounds = isOpaqueOverBounds() && mBitmap->isOpaque();
     }
 
@@ -959,11 +1050,9 @@
     SkBitmap* mBitmap;
     Res_png_9patch* mPatch;
 
-    int mAlpha;
-    SkXfermode::Mode mMode;
-
     uint32_t mGenerationId;
     const Patch* mMesh;
+
     AssetAtlas::Entry* mEntry;
 };
 
@@ -976,7 +1065,7 @@
         return renderer.drawColor(mColor, mMode);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw color %#x, mode %d", mColor, mMode);
     }
 
@@ -1021,7 +1110,7 @@
                 mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds));
     }
 
@@ -1044,7 +1133,7 @@
         return renderer.drawRects(mRects, mCount, getPaint(renderer));
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw Rects count %d", mCount);
     }
 
@@ -1070,7 +1159,7 @@
                 mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, getPaint(renderer));
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
     }
 
@@ -1091,7 +1180,7 @@
         return renderer.drawCircle(mX, mY, mRadius, getPaint(renderer));
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw Circle x %f, y %f, r %f", mX, mY, mRadius);
     }
 
@@ -1113,7 +1202,7 @@
                 mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds));
     }
 
@@ -1133,7 +1222,7 @@
                 mStartAngle, mSweepAngle, mUseCenter, getPaint(renderer));
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d",
                 RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter);
     }
@@ -1169,7 +1258,7 @@
         deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
     }
 
@@ -1191,7 +1280,7 @@
         return renderer.drawLines(mPoints, mCount, getPaint(renderer));
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw Lines count %d", mCount);
     }
 
@@ -1217,7 +1306,7 @@
         return renderer.drawPoints(mPoints, mCount, getPaint(renderer));
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw Points count %d", mCount);
     }
 
@@ -1229,7 +1318,7 @@
     DrawSomeTextOp(const char* text, int bytesCount, int count, SkPaint* paint)
             : DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {};
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw some text, %d bytes", mBytesCount);
     }
 
@@ -1339,7 +1428,7 @@
         return status;
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw Text of count %d, bytes %d", mCount, mBytesCount);
     }
 
@@ -1372,7 +1461,7 @@
         return ret;
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw Functor %p", mFunctor);
     }
 
@@ -1406,7 +1495,7 @@
         return DrawGlInfo::kStatusDone;
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw Display List %p, flags %#x", mDisplayList, mFlags);
         if (mDisplayList && (logFlags & kOpLogFlag_Recurse)) {
             mDisplayList->output(level + 1);
@@ -1429,7 +1518,7 @@
         return renderer.drawLayer(mLayer, mX, mY);
     }
 
-    virtual void output(int level, uint32_t logFlags) {
+    virtual void output(int level, uint32_t logFlags) const {
         OP_LOG("Draw Layer %p at %f %f", mLayer, mX, mY);
     }
 
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 6d85a16..5d23e1d 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -317,13 +317,10 @@
 
 status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
         float left, float top, float right, float bottom, SkPaint* paint) {
-    int alpha;
-    SkXfermode::Mode mode;
-    OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
-
     bitmap = refBitmap(bitmap);
+    paint = refPaint(paint);
 
-    addDrawOp(new (alloc()) DrawPatchOp(bitmap, patch, left, top, right, bottom, alpha, mode));
+    addDrawOp(new (alloc()) DrawPatchOp(bitmap, patch, left, top, right, bottom, paint));
     return DrawGlInfo::kStatusDone;
 }
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 1dcf21a..6f1dc6f 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -827,6 +827,7 @@
         if (!mSnapshot->isIgnored()) {
             mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
             mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
+            mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
         }
     }
 
@@ -1191,7 +1192,7 @@
         // after we setup drawing in case we need to mess with the
         // stencil buffer in setupDraw()
         TextureVertex* mesh = mCaches.getRegionMesh();
-        GLsizei numQuads = 0;
+        uint32_t numQuads = 0;
 
         setupDrawWithTexture();
         setupDrawColor(alpha, alpha, alpha, alpha);
@@ -2049,6 +2050,11 @@
             GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
 }
 
+/**
+ * Important note: this method is intended to draw batches of bitmaps and
+ * will not set the scissor enable or dirty the current layer, if any.
+ * The caller is responsible for properly dirtying the current layer.
+ */
 status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
         bool transformed, const Rect& bounds, SkPaint* paint) {
     mCaches.activeTexture(0);
@@ -2071,12 +2077,12 @@
         drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
                 texture->id, paint != NULL, color, alpha, mode,
                 &vertices[0].position[0], &vertices[0].texture[0],
-                GL_TRIANGLES, bitmapCount * 6, true, true);
+                GL_TRIANGLES, bitmapCount * 6, true, true, false);
     } else {
         drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
                 texture->id, alpha / 255.0f, mode, texture->blend,
                 &vertices[0].position[0], &vertices[0].texture[0],
-                GL_TRIANGLES, bitmapCount * 6, false, true, 0, true);
+                GL_TRIANGLES, bitmapCount * 6, false, true, 0, true, false);
     }
 
     return DrawGlInfo::kStatusDrew;
@@ -2367,10 +2373,6 @@
 
 status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
         float left, float top, float right, float bottom, SkPaint* paint) {
-    int alpha;
-    SkXfermode::Mode mode;
-    getAlphaAndMode(paint, &alpha, &mode);
-
     if (quickReject(left, top, right, bottom)) {
         return DrawGlInfo::kStatusDone;
     }
@@ -2379,13 +2381,11 @@
     const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(),
             right - left, bottom - top, patch);
 
-    return drawPatch(bitmap, mesh, entry, left, top, right, bottom, alpha, mode);
+    return drawPatch(bitmap, mesh, entry, left, top, right, bottom, paint);
 }
 
-status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh,
-        AssetAtlas::Entry* entry, float left, float top, float right, float bottom,
-        int alpha, SkXfermode::Mode mode) {
-
+status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
+        float left, float top, float right, float bottom, SkPaint* paint) {
     if (quickReject(left, top, right, bottom)) {
         return DrawGlInfo::kStatusDone;
     }
@@ -2399,6 +2399,10 @@
         texture->setWrap(GL_CLAMP_TO_EDGE, true);
         texture->setFilter(GL_LINEAR, true);
 
+        int alpha;
+        SkXfermode::Mode mode;
+        getAlphaAndMode(paint, &alpha, &mode);
+
         const bool pureTranslate = currentTransform().isPureTranslate();
         // Mark the current layer dirty where we are going to draw the patch
         if (hasLayer() && mesh->hasEmptyQuads) {
@@ -2418,8 +2422,6 @@
             }
         }
 
-        alpha *= mSnapshot->alpha;
-
         if (CC_LIKELY(pureTranslate)) {
             const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
             const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
@@ -2441,6 +2443,32 @@
     return DrawGlInfo::kStatusDrew;
 }
 
+/**
+ * Important note: this method is intended to draw batches of 9-patch objects and
+ * will not set the scissor enable or dirty the current layer, if any.
+ * The caller is responsible for properly dirtying the current layer.
+ */
+status_t OpenGLRenderer::drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry,
+        TextureVertex* vertices, uint32_t indexCount, SkPaint* paint) {
+    mCaches.activeTexture(0);
+    Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
+    if (!texture) return DrawGlInfo::kStatusDone;
+    const AutoTexture autoCleanup(texture);
+
+    texture->setWrap(GL_CLAMP_TO_EDGE, true);
+    texture->setFilter(GL_LINEAR, true);
+
+    int alpha;
+    SkXfermode::Mode mode;
+    getAlphaAndMode(paint, &alpha, &mode);
+
+    drawIndexedTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, alpha / 255.0f,
+            mode, texture->blend, &vertices[0].position[0], &vertices[0].texture[0],
+            GL_TRIANGLES, indexCount, false, true, 0, true, false);
+
+    return DrawGlInfo::kStatusDrew;
+}
+
 status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint,
         bool useOffset) {
     if (!vertexBuffer.getVertexCount()) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 0760f9c..aa0f9fc 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -297,10 +297,12 @@
     virtual status_t drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint);
     virtual status_t drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
             float* vertices, int* colors, SkPaint* paint);
+    status_t drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry,
+            TextureVertex* vertices, uint32_t indexCount, SkPaint* paint);
     virtual status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
             float left, float top, float right, float bottom, SkPaint* paint);
     status_t drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
-            float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode);
+            float left, float top, float right, float bottom, SkPaint* paint);
     virtual status_t drawColor(int color, SkXfermode::Mode mode);
     virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint);
     virtual status_t drawRoundRect(float left, float top, float right, float bottom,
@@ -358,6 +360,9 @@
         return mSnapshot->clipRegion->isEmpty();
     }
 
+    int getViewportWidth() { return getSnapshot()->viewport.getWidth(); }
+    int getViewportHeight() { return getSnapshot()->viewport.getHeight(); }
+
     /**
      * Scales the alpha on the current snapshot. This alpha value will be modulated
      * with other alpha values when drawing primitives.
@@ -1115,6 +1120,8 @@
     friend class DisplayListRenderer;
     friend class Layer;
     friend class TextSetupFunctor;
+    friend class DrawBitmapOp;
+    friend class DrawPatchOp;
 
 }; // class OpenGLRenderer
 
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 6b0734a..9e3e701 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -32,7 +32,7 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-Patch::Patch(): verticesCount(0), indexCount(0), hasEmptyQuads(false) {
+Patch::Patch(): vertices(NULL), verticesCount(0), indexCount(0), hasEmptyQuads(false) {
 }
 
 Patch::~Patch() {
@@ -47,14 +47,14 @@
 }
 
 TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight,
-        float left, float top, float right, float bottom, const Res_png_9patch* patch) {
+        float width, float height, const Res_png_9patch* patch) {
     UvMapper mapper;
-    return createMesh(bitmapWidth, bitmapHeight, left, top, right, bottom, mapper, patch);
+    return createMesh(bitmapWidth, bitmapHeight, width, height, mapper, patch);
 }
 
 TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight,
-        float left, float top, float right, float bottom,
-        const UvMapper& mapper, const Res_png_9patch* patch) {
+        float width, float height, const UvMapper& mapper, const Res_png_9patch* patch) {
+    if (vertices) return vertices;
 
     const uint32_t* colors = &patch->colors[0];
     const int8_t numColors = patch->numColors;
@@ -79,7 +79,7 @@
     uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 4;
     if (maxVertices == 0) return NULL;
 
-    TextureVertex* vertices = new TextureVertex[maxVertices];
+    vertices = new TextureVertex[maxVertices];
     TextureVertex* vertex = vertices;
 
     const int32_t* xDivs = patch->xDivs;
@@ -101,9 +101,9 @@
         }
         const float xStretchTex = stretchSize;
         const float fixed = bitmapWidth - stretchSize;
-        const float xStretch = fmaxf(right - left - fixed, 0.0f);
+        const float xStretch = fmaxf(width - fixed, 0.0f);
         stretchX = xStretch / xStretchTex;
-        rescaleX = fixed == 0.0f ? 0.0f : fminf(fmaxf(right - left, 0.0f) / fixed, 1.0f);
+        rescaleX = fixed == 0.0f ? 0.0f : fminf(fmaxf(width, 0.0f) / fixed, 1.0f);
     }
 
     if (yStretchCount > 0) {
@@ -113,9 +113,9 @@
         }
         const float yStretchTex = stretchSize;
         const float fixed = bitmapHeight - stretchSize;
-        const float yStretch = fmaxf(bottom - top - fixed, 0.0f);
+        const float yStretch = fmaxf(height - fixed, 0.0f);
         stretchY = yStretch / yStretchTex;
-        rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(bottom - top, 0.0f) / fixed, 1.0f);
+        rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(height, 0.0f) / fixed, 1.0f);
     }
 
     uint32_t quadCount = 0;
@@ -144,7 +144,7 @@
 
         if (stepY > 0.0f) {
             generateRow(xDivs, xCount, vertex, y1, y2, v1, v2, stretchX, rescaleX,
-                    right - left, bitmapWidth, quadCount);
+                    width, bitmapWidth, quadCount);
         }
 
         y1 = y2;
@@ -154,9 +154,9 @@
     }
 
     if (previousStepY != bitmapHeight) {
-        y2 = bottom - top;
+        y2 = height;
         generateRow(xDivs, xCount, vertex, y1, y2, v1, 1.0f, stretchX, rescaleX,
-                right - left, bitmapWidth, quadCount);
+                width, bitmapWidth, quadCount);
     }
 
     return vertices;
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index 448cf60..246ba66 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -45,6 +45,7 @@
      */
     uint32_t getSize() const;
 
+    TextureVertex* vertices;
     uint32_t verticesCount;
     uint32_t indexCount;
     bool hasEmptyQuads;
@@ -54,11 +55,9 @@
     GLintptr textureOffset;
 
     TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight,
-            float left, float top, float right, float bottom,
-            const Res_png_9patch* patch);
+            float width, float height, const Res_png_9patch* patch);
     TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight,
-            float left, float top, float right, float bottom,
-            const UvMapper& mapper, const Res_png_9patch* patch);
+            float width, float height, const UvMapper& mapper, const Res_png_9patch* patch);
 
 private:
     void generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex,
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index c23e991..dc69d7f 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -118,10 +118,10 @@
 
         if (entry) {
             vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
-                    0.0f, 0.0f, pixelWidth, pixelHeight, entry->uvMapper, patch);
+                    pixelWidth, pixelHeight, entry->uvMapper, patch);
         } else {
             vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
-                    0.0f, 0.0f, pixelWidth, pixelHeight, patch);
+                    pixelWidth, pixelHeight, patch);
         }
 
         if (vertices) {
@@ -141,8 +141,6 @@
             mSize += size;
 
             glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
-
-            delete[] vertices;
         }
 
         mCache.put(description, newMesh);
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
index 74b628a..36e89c6 100644
--- a/libs/hwui/PixelBuffer.cpp
+++ b/libs/hwui/PixelBuffer.cpp
@@ -132,7 +132,10 @@
     if (mAccessMode != kAccessMode_None) {
         if (mMappedPointer) {
             mCaches.bindPixelBuffer(mBuffer);
-            glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
+            GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
+            if (status == GL_FALSE) {
+                ALOGE("Corrupted GPU pixel buffer");
+            }
         }
         mAccessMode = kAccessMode_None;
         mMappedPointer = NULL;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index a19b52a..0e40140 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -90,7 +90,6 @@
     boolean mShowPhoneRSSIForData = false;
     boolean mShowAtLeastThreeGees = false;
     boolean mAlwaysShowCdmaRssi = false;
-    boolean mShow4GforLTE = false;
 
     String mContentDescriptionPhoneSignal;
     String mContentDescriptionWifi;
@@ -200,7 +199,6 @@
 
         mShowPhoneRSSIForData = res.getBoolean(R.bool.config_showPhoneRSSIForData);
         mShowAtLeastThreeGees = res.getBoolean(R.bool.config_showMin3G);
-        mShow4GforLTE = res.getBoolean(R.bool.config_show4GForLTE);
         mAlwaysShowCdmaRssi = res.getBoolean(
                 com.android.internal.R.bool.config_alwaysUseCdmaRssi);
 
@@ -680,7 +678,8 @@
                             R.string.accessibility_data_connection_3g);
                     break;
                 case TelephonyManager.NETWORK_TYPE_LTE:
-                    if (mShow4GforLTE) {
+                    boolean show4GforLTE = mContext.getResources().getBoolean(R.bool.config_show4GForLTE);
+                    if (show4GforLTE) {
                         mDataIconList = TelephonyIcons.DATA_4G[mInetCondition];
                         mDataTypeIconId = R.drawable.stat_sys_data_connected_4g;
                         mQSDataTypeIconId = R.drawable.ic_qs_signal_4g;
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 55680c8..b456bb3 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -94,7 +94,7 @@
      * @hide
      */
     public void initialize(SignalStrength ss, int timingAdvance) {
-        mSignalStrength = ss.getLteSignalStrenght();
+        mSignalStrength = ss.getLteSignalStrength();
         mRsrp = ss.getLteRsrp();
         mRsrq = ss.getLteRsrq();
         mRssnr = ss.getLteRssnr();
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 674955c..7b9cf4e 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -438,7 +438,7 @@
     }
 
     /** @hide */
-    public int getLteSignalStrenght() {
+    public int getLteSignalStrength() {
         return mLteSignalStrength;
     }