Merge "Add plumbing for volume mute key."
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 007e7b9..f80fbd8 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -129,6 +129,7 @@
     private long[] mVibePattern;
 
     private int mAspect;
+    private final Matrix mArrowMatrix = new Matrix();
 
     /**
      * Represents a cell in the 3 X 3 matrix of the unlock pattern view.
@@ -923,7 +924,6 @@
         // This assumes that the arrow image is drawn at 12:00 with it's top edge
         // coincident with the circle bitmap's top edge.
         Bitmap arrow = green ? mBitmapArrowGreenUp : mBitmapArrowRedUp;
-        Matrix matrix = new Matrix();
         final int cellWidth = mBitmapCircleDefault.getWidth();
         final int cellHeight = mBitmapCircleDefault.getHeight();
 
@@ -933,10 +933,10 @@
         final float angle = (float) Math.toDegrees(theta) + 90.0f;
 
         // compose matrix
-        matrix.setTranslate(leftX + offsetX, topY + offsetY); // transform to cell position
-        matrix.preRotate(angle, cellWidth / 2.0f, cellHeight / 2.0f);  // rotate about cell center
-        matrix.preTranslate((cellWidth - arrow.getWidth()) / 2.0f, 0.0f); // translate to 12:00 pos
-        canvas.drawBitmap(arrow, matrix, mPaint);
+        mArrowMatrix.setTranslate(leftX + offsetX, topY + offsetY); // transform to cell position
+        mArrowMatrix.preRotate(angle, cellWidth / 2.0f, cellHeight / 2.0f);  // rotate about cell center
+        mArrowMatrix.preTranslate((cellWidth - arrow.getWidth()) / 2.0f, 0.0f); // translate to 12:00 pos
+        canvas.drawBitmap(arrow, mArrowMatrix, mPaint);
     }
 
     /**
diff --git a/core/res/res/layout/text_edit_no_paste_window.xml b/core/res/res/layout/text_edit_no_paste_window.xml
index 84b6103..d409d97 100644
--- a/core/res/res/layout/text_edit_no_paste_window.xml
+++ b/core/res/res/layout/text_edit_no_paste_window.xml
@@ -40,6 +40,7 @@
         android:layout_centerVertical="true"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:text="@android:string/pasteDisabled"
+        android:textColor="@android:color/dim_foreground_dark_inverse_disabled"
         android:layout_toRightOf="@id/paste_icon"
     />
 
diff --git a/core/res/res/layout/text_edit_paste_window.xml b/core/res/res/layout/text_edit_paste_window.xml
index 369f4a5..d153365 100644
--- a/core/res/res/layout/text_edit_paste_window.xml
+++ b/core/res/res/layout/text_edit_paste_window.xml
@@ -40,6 +40,7 @@
         android:layout_centerVertical="true"
         android:textAppearance="?android:attr/textAppearanceMediumInverse"
         android:text="@android:string/paste"
+        android:textColor="@android:color/black"
         android:layout_toRightOf="@id/paste_icon"
     />
 
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java
index 6adfc74..3a78f26 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java
@@ -20,6 +20,7 @@
 import android.test.InstrumentationTestRunner;
 import android.test.InstrumentationTestSuite;
 import android.util.Log;
+import com.android.connectivitymanagertest.unit.WifiClientTest;
 import com.android.connectivitymanagertest.unit.WifiSoftAPTest;
 
 import junit.framework.TestSuite;
@@ -35,6 +36,7 @@
     @Override
     public TestSuite getAllTests() {
         TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(WifiClientTest.class);
         suite.addTestSuite(WifiSoftAPTest.class);
         return suite;
     }
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
new file mode 100644
index 0000000..6717bda
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2010 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 com.android.connectivitymanagertest.unit;
+
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.Context;
+import android.app.Instrumentation;
+import android.os.Handler;
+import android.os.Message;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiConfiguration.Status;
+
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.AndroidTestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.util.Log;
+
+/**
+ * Test wifi client
+ */
+public class WifiClientTest extends AndroidTestCase {
+
+    private WifiManager mWifiManager;
+    private final String TAG = "WifiClientTest";
+
+    //10s delay for turning on wifi
+    private static final int DELAY = 10000;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+        mWifiManager.setWifiEnabled(true);
+        assertNotNull(mWifiManager);
+    }
+
+    private void sleepAfterWifiEnable() {
+        try {
+            Thread.sleep(DELAY);
+        } catch (Exception e) {
+            fail("Sleep timeout " + e);
+        }
+    }
+
+    // Test case 1: add/remove a open network
+    @LargeTest
+    public void testAddRemoveNetwork() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = "\"TestSSID1\"";
+        config.allowedKeyManagement.set(KeyMgmt.NONE);
+
+        //add
+        int netId = mWifiManager.addNetwork(config);
+        assertTrue(netId != -1);
+
+        //check config list
+        List<WifiConfiguration> configList = mWifiManager.getConfiguredNetworks();
+        boolean found = false;
+        for (WifiConfiguration c : configList) {
+            if (c.networkId == netId) {
+                found = true;
+            }
+        }
+        assertTrue(found);
+
+        //remove
+        boolean ret = mWifiManager.removeNetwork(netId);
+        assertTrue(ret);
+
+        //check config list
+        configList = mWifiManager.getConfiguredNetworks();
+        found = false;
+        for (WifiConfiguration c : configList) {
+            if (c.networkId == netId) {
+                found = true;
+            }
+        }
+
+        assertFalse(found);
+    }
+
+    // Test case 2: enable/disable a open network
+    @LargeTest
+    public void testEnableDisableNetwork() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = "\"TestSSID2\"";
+        config.allowedKeyManagement.set(KeyMgmt.NONE);
+
+        //add
+        int netId = mWifiManager.addNetwork(config);
+        assertTrue(netId != -1);
+
+        //enable network and disable others
+        boolean ret = mWifiManager.enableNetwork(netId, true);
+        assertTrue(ret);
+
+        //check config list
+        List<WifiConfiguration> configList = mWifiManager.getConfiguredNetworks();
+        for (WifiConfiguration c : configList) {
+            if (c.networkId == netId) {
+                assertTrue(c.status == Status.ENABLED);
+            } else {
+                assertFalse(c.status == Status.ENABLED);
+            }
+        }
+
+        //disable network
+        ret = mWifiManager.disableNetwork(netId);
+        assertTrue(ret);
+
+        //check config list
+        configList = mWifiManager.getConfiguredNetworks();
+        for (WifiConfiguration c : configList) {
+            if (c.networkId == netId) {
+                assertTrue(c.status == Status.DISABLED);
+            }
+        }
+    }
+
+    // Test case 3: ping supplicant
+    @LargeTest
+    public void testPingSupplicant() {
+        assertTrue(mWifiManager.pingSupplicant());
+        mWifiManager.setWifiEnabled(false);
+        sleepAfterWifiEnable();
+
+        assertFalse(mWifiManager.pingSupplicant());
+        mWifiManager.setWifiEnabled(true);
+        sleepAfterWifiEnable();
+    }
+
+    // Test case 4: save config
+    @LargeTest
+    public void testSaveConfig() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = "\"TestSSID3\"";
+        config.allowedKeyManagement.set(KeyMgmt.NONE);
+
+        //add
+        int netId = mWifiManager.addNetwork(config);
+        assertTrue(netId != -1);
+
+        mWifiManager.saveConfiguration();
+
+        //restart wifi
+        mWifiManager.setWifiEnabled(false);
+        mWifiManager.setWifiEnabled(true);
+
+        sleepAfterWifiEnable();
+
+        //check config list
+        List<WifiConfiguration> configList = mWifiManager.getConfiguredNetworks();
+        boolean found = false;
+        for (WifiConfiguration c : configList) {
+            if (c.SSID.equals("TestSSID3")) {
+                found = true;
+            }
+        }
+        assertTrue(found);
+
+        //restore config
+        boolean ret = mWifiManager.removeNetwork(netId);
+        assertTrue(ret);
+        mWifiManager.saveConfiguration();
+    }
+}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index d661f7b..29158e5 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -38,7 +38,7 @@
 
 	LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER
 	LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-	LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia
+	LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui
 	LOCAL_MODULE := libhwui
 	LOCAL_MODULE_TAGS := optional
 	LOCAL_PRELINK_MODULE := false
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 0994d82..93c5b34 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -46,18 +46,21 @@
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
 
     mCurrentBuffer = meshBuffer;
+    mRegionMesh = NULL;
 }
 
-/**
- * Binds the VBO used to render simple textured quads.
- */
+Caches::~Caches() {
+    delete[] mRegionMesh;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// VBO
+///////////////////////////////////////////////////////////////////////////////
+
 void Caches::bindMeshBuffer() {
     bindMeshBuffer(meshBuffer);
 }
 
-/**
- * Binds the specified VBO.
- */
 void Caches::bindMeshBuffer(const GLuint buffer) {
     if (mCurrentBuffer != buffer) {
         glBindBuffer(GL_ARRAY_BUFFER, buffer);
@@ -65,9 +68,6 @@
     }
 }
 
-/**
- * Unbinds the VBO used to render simple textured quads.
- */
 void Caches::unbindMeshBuffer() {
     if (mCurrentBuffer) {
         glBindBuffer(GL_ARRAY_BUFFER, 0);
@@ -75,5 +75,35 @@
     }
 }
 
+TextureVertex* Caches::getRegionMesh() {
+    // Create the mesh, 2 triangles and 4 vertices per rectangle in the region
+    if (!mRegionMesh) {
+        mRegionMesh = new TextureVertex[REGION_MESH_QUAD_COUNT * 4];
+
+        uint16_t* regionIndices = new uint16_t[REGION_MESH_QUAD_COUNT * 6];
+        for (int i = 0; i < REGION_MESH_QUAD_COUNT; i++) {
+            uint16_t quad = i * 4;
+            int index = i * 6;
+            regionIndices[index    ] = quad;       // top-left
+            regionIndices[index + 1] = quad + 1;   // top-right
+            regionIndices[index + 2] = quad + 2;   // bottom-left
+            regionIndices[index + 3] = quad + 2;   // bottom-left
+            regionIndices[index + 4] = quad + 1;   // top-right
+            regionIndices[index + 5] = quad + 3;   // bottom-right
+        }
+
+        glGenBuffers(1, &mRegionMeshIndices);
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mRegionMeshIndices);
+        glBufferData(GL_ELEMENT_ARRAY_BUFFER, REGION_MESH_QUAD_COUNT * 6 * sizeof(uint16_t),
+                regionIndices, GL_STATIC_DRAW);
+
+        delete[] regionIndices;
+    } else {
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mRegionMeshIndices);
+    }
+
+    return mRegionMesh;
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index ca22867..c019fd1 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_CACHES_H
-#define ANDROID_UI_CACHES_H
+#ifndef ANDROID_HWUI_CACHES_H
+#define ANDROID_HWUI_CACHES_H
 
 #ifndef LOG_TAG
     #define LOG_TAG "OpenGLRenderer"
@@ -46,6 +46,8 @@
 
 #define REQUIRED_TEXTURE_UNITS_COUNT 3
 
+#define REGION_MESH_QUAD_COUNT 512
+
 // Generates simple and textured vertices
 #define FV(x, y, u, v) { { x, y }, { u, v } }
 
@@ -77,6 +79,7 @@
 
 class Caches: public Singleton<Caches> {
     Caches();
+    ~Caches();
 
     friend class Singleton<Caches>;
 
@@ -84,11 +87,33 @@
 
     GLuint mCurrentBuffer;
 
+    // Used to render layers
+    TextureVertex* mRegionMesh;
+    GLuint mRegionMeshIndices;
+
 public:
+    /**
+     * Binds the VBO used to render simple textured quads.
+     */
     void bindMeshBuffer();
+
+    /**
+     * Binds the specified VBO if needed.
+     */
     void bindMeshBuffer(const GLuint buffer);
+
+    /**
+     * Unbinds the VBO used to render simple textured quads.
+     */
     void unbindMeshBuffer();
 
+    /**
+     * Returns the mesh used to draw regions. Calling this method will
+     * bind a VBO of type GL_ELEMENT_ARRAY_BUFFER that contains the
+     * indices for the region mesh.
+     */
+    TextureVertex* getRegionMesh();
+
     bool blend;
     GLenum lastSrcMode;
     GLenum lastDstMode;
@@ -118,7 +143,6 @@
 }; // class Caches
 
 }; // namespace uirenderer
-
 }; // namespace android
 
-#endif // ANDROID_UI_CACHES_H
+#endif // ANDROID_HWUI_CACHES_H
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 0dd9c5a..23ccef6 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -169,7 +169,8 @@
     int saveCount = renderer.getSaveCount() - 1;
 
     while (!mReader.eof()) {
-        switch (mReader.readInt()) {
+        int op = mReader.readInt();
+        switch (op) {
             case AcquireContext: {
                 renderer.acquireContext();
             }
@@ -195,6 +196,11 @@
                         getPaint(), getInt());
             }
             break;
+            case SaveLayerAlpha: {
+                renderer.saveLayerAlpha(getFloat(), getFloat(), getFloat(), getFloat(),
+                        getInt(), getInt());
+            }
+            break;
             case Translate: {
                 renderer.translate(getFloat(), getFloat());
             }
@@ -400,6 +406,15 @@
     return OpenGLRenderer::save(flags);
 }
 
+int DisplayListRenderer::saveLayerAlpha(float left, float top, float right, float bottom,
+        int alpha, int flags) {
+    addOp(DisplayList::SaveLayerAlpha);
+    addBounds(left, top, right, bottom);
+    addInt(alpha);
+    addInt(flags);
+    return OpenGLRenderer::save(flags);
+}
+
 void DisplayListRenderer::translate(float dx, float dy) {
     addOp(DisplayList::Translate);
     addPoint(dx, dy);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 6636de6..fd69fab 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_DISPLAY_LIST_RENDERER_H
-#define ANDROID_UI_DISPLAY_LIST_RENDERER_H
+#ifndef ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
+#define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
 
 #include <SkChunkAlloc.h>
 #include <SkFlattenable.h>
@@ -85,6 +85,7 @@
         Restore,
         RestoreToCount,
         SaveLayer,
+        SaveLayerAlpha,
         Translate,
         Rotate,
         Scale,
@@ -222,6 +223,8 @@
 
     int saveLayer(float left, float top, float right, float bottom,
             SkPaint* p, int flags);
+    int saveLayerAlpha(float left, float top, float right, float bottom,
+                int alpha, int flags);
 
     void translate(float dx, float dy);
     void rotate(float degrees);
@@ -411,4 +414,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_DISPLAY_LIST_RENDERER_H
+#endif // ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index d50d36e..eceb5c1c 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_EXTENSIONS_H
-#define ANDROID_UI_EXTENSIONS_H
+#ifndef ANDROID_HWUI_EXTENSIONS_H
+#define ANDROID_HWUI_EXTENSIONS_H
 
 #include <utils/SortedVector.h>
 #include <utils/String8.h>
@@ -93,4 +93,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_EXTENSIONS_H
+#endif // ANDROID_HWUI_EXTENSIONS_H
diff --git a/libs/hwui/FboCache.h b/libs/hwui/FboCache.h
index ec4afe9..ad6cc3e 100644
--- a/libs/hwui/FboCache.h
+++ b/libs/hwui/FboCache.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_FBO_CACHE_H
-#define ANDROID_UI_FBO_CACHE_H
+#ifndef ANDROID_HWUI_FBO_CACHE_H
+#define ANDROID_HWUI_FBO_CACHE_H
 
 #include <GLES2/gl2.h>
 
@@ -76,4 +76,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_FBO_CACHE_H
+#endif // ANDROID_HWUI_FBO_CACHE_H
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index e1a236c..5224689 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -556,6 +556,8 @@
 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
     glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
+
+    mDrawn = true;
 }
 
 void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
@@ -595,6 +597,13 @@
 
     mCurrentQuadIndex++;
 
+    if (mBounds) {
+        mBounds->left = fmin(mBounds->left, x1);
+        mBounds->top = fmin(mBounds->top, y3);
+        mBounds->right = fmax(mBounds->right, x3);
+        mBounds->bottom = fmax(mBounds->bottom, y1);
+    }
+
     if (mCurrentQuadIndex == mMaxNumberOfQuads) {
         issueDrawCommand();
         mCurrentQuadIndex = 0;
@@ -674,22 +683,27 @@
     return image;
 }
 
-void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
-        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
+bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
+        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
     checkInit();
 
     if (!mCurrentFont) {
         LOGE("No font set");
-        return;
+        return false;
     }
 
+    mDrawn = false;
+    mBounds = bounds;
     mClip = clip;
     mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
+    mBounds = NULL;
 
     if (mCurrentQuadIndex != 0) {
         issueDrawCommand();
         mCurrentQuadIndex = 0;
     }
+
+    return mDrawn;
 }
 
 void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index f10efad..a76cb86 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_FONT_RENDERER_H
-#define ANDROID_UI_FONT_RENDERER_H
+#ifndef ANDROID_HWUI_FONT_RENDERER_H
+#define ANDROID_HWUI_FONT_RENDERER_H
 
 #include <utils/String8.h>
 #include <utils/String16.h>
@@ -132,8 +132,8 @@
     }
 
     void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
-    void renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
-            uint32_t len, int numGlyphs, int x, int y);
+    bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
+            uint32_t len, int numGlyphs, int x, int y, Rect* bounds);
 
     struct DropShadow {
         DropShadow() { };
@@ -257,6 +257,8 @@
     uint32_t mIndexBufferID;
 
     const Rect* mClip;
+    Rect* mBounds;
+    bool mDrawn;
 
     bool mInitialized;
 
@@ -273,4 +275,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_FONT_RENDERER_H
+#endif // ANDROID_HWUI_FONT_RENDERER_H
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index 5fa45cf..b59ae81 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_GAMMA_FONT_RENDERER_H
-#define ANDROID_UI_GAMMA_FONT_RENDERER_H
+#ifndef ANDROID_HWUI_GAMMA_FONT_RENDERER_H
+#define ANDROID_HWUI_GAMMA_FONT_RENDERER_H
 
 #include <SkPaint.h>
 
@@ -45,4 +45,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_GAMMA_FONT_RENDERER_H
+#endif // ANDROID_HWUI_GAMMA_FONT_RENDERER_H
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 48877f6d3..c9553f4 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_GRADIENT_CACHE_H
-#define ANDROID_UI_GRADIENT_CACHE_H
+#ifndef ANDROID_HWUI_GRADIENT_CACHE_H
+#define ANDROID_HWUI_GRADIENT_CACHE_H
 
 #include <SkShader.h>
 
@@ -93,4 +93,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_GRADIENT_CACHE_H
+#endif // ANDROID_HWUI_GRADIENT_CACHE_H
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 2afe2fa..a780183 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -14,13 +14,15 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_LAYER_H
-#define ANDROID_UI_LAYER_H
+#ifndef ANDROID_HWUI_LAYER_H
+#define ANDROID_HWUI_LAYER_H
 
 #include <sys/types.h>
 
 #include <GLES2/gl2.h>
 
+#include <ui/Region.h>
+
 #include <SkXfermode.h>
 
 #include "Rect.h"
@@ -85,9 +87,15 @@
      * Height of the layer texture.
      */
     uint32_t height;
+
+    /**
+     * Dirty region indicating what parts of the layer
+     * have been drawn.
+     */
+    Region region;
 }; // struct Layer
 
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_LAYER_H
+#endif // ANDROID_HWUI_LAYER_H
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index ae792ab..4df8ab3 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_LAYER_CACHE_H
-#define ANDROID_UI_LAYER_CACHE_H
+#ifndef ANDROID_HWUI_LAYER_CACHE_H
+#define ANDROID_HWUI_LAYER_CACHE_H
 
 #include "Layer.h"
 #include "utils/SortedList.h"
@@ -138,4 +138,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_LAYER_CACHE_H
+#endif // ANDROID_HWUI_LAYER_CACHE_H
diff --git a/libs/hwui/Line.h b/libs/hwui/Line.h
index c529354..5c6f3d8 100644
--- a/libs/hwui/Line.h
+++ b/libs/hwui/Line.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_LINE_H
-#define ANDROID_UI_LINE_H
+#ifndef ANDROID_HWUI_LINE_H
+#define ANDROID_HWUI_LINE_H
 
 #include <GLES2/gl2.h>
 
@@ -123,4 +123,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_LINE_H
+#endif // ANDROID_HWUI_LINE_H
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 83ea615..7462d5b 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -271,6 +271,19 @@
         MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]);
         MUL_ADD_STORE(r.top, data[kScaleY], data[kTranslateY]);
         MUL_ADD_STORE(r.bottom, data[kScaleY], data[kTranslateY]);
+
+        if (r.left > r.right) {
+            float x = r.left;
+            r.left = r.right;
+            r.right = x;
+        }
+
+        if (r.top > r.bottom) {
+            float y = r.top;
+            r.top = r.bottom;
+            r.bottom = y;
+        }
+
         return;
     }
 
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index fe81159..d678bd0 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_MATRIX_H
-#define ANDROID_UI_MATRIX_H
+#ifndef ANDROID_HWUI_MATRIX_H
+#define ANDROID_HWUI_MATRIX_H
 
 #include <SkMatrix.h>
 
@@ -137,4 +137,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_MATRIX_H
+#endif // ANDROID_HWUI_MATRIX_H
diff --git a/libs/hwui/OpenGLDebugRenderer.h b/libs/hwui/OpenGLDebugRenderer.h
index ce6a4aa..7787ff1 100644
--- a/libs/hwui/OpenGLDebugRenderer.h
+++ b/libs/hwui/OpenGLDebugRenderer.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_OPENGL_DEBUG_RENDERER_H
-#define ANDROID_UI_OPENGL_DEBUG_RENDERER_H
+#ifndef ANDROID_HWUI_OPENGL_DEBUG_RENDERER_H
+#define ANDROID_HWUI_OPENGL_DEBUG_RENDERER_H
 
 #include "OpenGLRenderer.h"
 
@@ -66,4 +66,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_OPENGL_DEBUG_RENDERER_H
+#endif // ANDROID_HWUI_OPENGL_DEBUG_RENDERER_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index b167131..0f0316f 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -26,6 +26,8 @@
 #include <utils/Log.h>
 #include <utils/StopWatch.h>
 
+#include <ui/Rect.h>
+
 #include "OpenGLRenderer.h"
 
 namespace android {
@@ -285,7 +287,7 @@
 
 int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bottom,
         int alpha, int flags) {
-    if (alpha == 0xff) {
+    if (alpha >= 255 - ALPHA_THRESHOLD) {
         return saveLayer(left, top, right, bottom, NULL, flags);
     } else {
         SkPaint paint;
@@ -377,7 +379,6 @@
             bounds.getHeight() > mCaches.maxTextureSize) {
         snapshot->invisible = true;
     } else {
-        // TODO: Should take the mode into account
         snapshot->invisible = snapshot->previous->invisible ||
                 (alpha <= ALPHA_THRESHOLD && fboLayer);
     }
@@ -387,8 +388,7 @@
         return false;
     }
 
-    glActiveTexture(GL_TEXTURE0);
-
+    glActiveTexture(gTextureUnits[0]);
     Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight());
     if (!layer) {
         return false;
@@ -405,56 +405,7 @@
     snapshot->layer = layer;
 
     if (fboLayer) {
-        layer->fbo = mCaches.fboCache.get();
-
-        snapshot->flags |= Snapshot::kFlagIsFboLayer;
-        snapshot->fbo = layer->fbo;
-        snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
-        snapshot->resetClip(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
-        snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
-        snapshot->height = bounds.getHeight();
-        snapshot->flags |= Snapshot::kFlagDirtyOrtho;
-        snapshot->orthoMatrix.load(mOrthoMatrix);
-
-        // Bind texture to FBO
-        glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
-        glBindTexture(GL_TEXTURE_2D, layer->texture);
-
-        // Initialize the texture if needed
-        if (layer->empty) {
-            layer->empty = false;
-            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0,
-                    GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-        }
-
-        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                layer->texture, 0);
-
-#if DEBUG_LAYERS
-        GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
-        if (status != GL_FRAMEBUFFER_COMPLETE) {
-            LOGE("Framebuffer incomplete (GL error code 0x%x)", status);
-
-            glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
-            glDeleteTextures(1, &layer->texture);
-            mCaches.fboCache.put(layer->fbo);
-
-            delete layer;
-
-            return false;
-        }
-#endif
-
-        // Clear the FBO
-        glScissor(0.0f, 0.0f, bounds.getWidth() + 1.0f, bounds.getHeight() + 1.0f);
-        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-        glClear(GL_COLOR_BUFFER_BIT);
-
-        dirtyClip();
-
-        // Change the ortho projection
-        glViewport(0, 0, bounds.getWidth(), bounds.getHeight());
-        mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f);
+        return createFboLayer(layer, bounds, snapshot, previousFbo);
     } else {
         // Copy the framebuffer into the layer
         glBindTexture(GL_TEXTURE_2D, layer->texture);
@@ -475,6 +426,82 @@
     return true;
 }
 
+bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot,
+        GLuint previousFbo) {
+    layer->fbo = mCaches.fboCache.get();
+
+#if RENDER_LAYERS_AS_REGIONS
+    snapshot->region = &snapshot->layer->region;
+    snapshot->flags |= Snapshot::kFlagFboTarget;
+#endif
+
+    Rect clip(bounds);
+    snapshot->transform->mapRect(clip);
+    clip.intersect(*snapshot->clipRect);
+    clip.snapToPixelBoundaries();
+    clip.intersect(snapshot->previous->viewport);
+
+    mat4 inverse;
+    inverse.loadInverse(*mSnapshot->transform);
+
+    inverse.mapRect(clip);
+    clip.snapToPixelBoundaries();
+    clip.intersect(bounds);
+
+    snapshot->flags |= Snapshot::kFlagIsFboLayer;
+    snapshot->fbo = layer->fbo;
+    snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
+    //snapshot->resetClip(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
+    snapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
+    snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
+    snapshot->height = bounds.getHeight();
+    snapshot->flags |= Snapshot::kFlagDirtyOrtho;
+    snapshot->orthoMatrix.load(mOrthoMatrix);
+
+    // Bind texture to FBO
+    glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+    glBindTexture(GL_TEXTURE_2D, layer->texture);
+
+    // Initialize the texture if needed
+    if (layer->empty) {
+        layer->empty = false;
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0,
+                GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+    }
+
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+            layer->texture, 0);
+
+#if DEBUG_LAYERS_AS_REGIONS
+    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    if (status != GL_FRAMEBUFFER_COMPLETE) {
+        LOGE("Framebuffer incomplete (GL error code 0x%x)", status);
+
+        glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+        glDeleteTextures(1, &layer->texture);
+        mCaches.fboCache.put(layer->fbo);
+
+        delete layer;
+
+        return false;
+    }
+#endif
+
+    // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
+    glScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
+            clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    dirtyClip();
+
+    // Change the ortho projection
+    glViewport(0, 0, bounds.getWidth(), bounds.getHeight());
+    mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f);
+
+    return true;
+}
+
 /**
  * Read the documentation of createLayer() before doing anything in this method.
  */
@@ -484,42 +511,36 @@
         return;
     }
 
-    const bool fboLayer = current->flags & SkCanvas::kClipToLayer_SaveFlag;
+    const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer;
 
     if (fboLayer) {
         // Unbind current FBO and restore previous one
         glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
     }
 
-    // Restore the clip from the previous snapshot
-    Rect& clip(*previous->clipRect);
-    clip.snapToPixelBoundaries();
-    glScissor(clip.left, previous->height - clip.bottom, clip.getWidth(), clip.getHeight());
-
     Layer* layer = current->layer;
     const Rect& rect = layer->layer;
 
     if (!fboLayer && layer->alpha < 255) {
         drawColorRect(rect.left, rect.top, rect.right, rect.bottom,
                 layer->alpha << 24, SkXfermode::kDstIn_Mode, true);
+        // Required below, composeLayerRect() will divide by 255
+        layer->alpha = 255;
     }
 
-    const Rect& texCoords = layer->texCoords;
     mCaches.unbindMeshBuffer();
-    resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom);
 
     glActiveTexture(gTextureUnits[0]);
-    if (fboLayer) {
-        drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture,
-                layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0],
-                &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount);
-    } else {
-        drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture,
-                1.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0],
-                &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, true, true);
-    }
 
-    resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+    // When the layer is stored in an FBO, we can save a bit of fillrate by
+    // drawing only the dirty region
+    if (fboLayer) {
+        dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *previous->transform);
+        composeLayerRegion(layer, rect);
+    } else {
+        dirtyLayer(rect.left, rect.top, rect.right, rect.bottom);
+        composeLayerRect(layer, rect, true);
+    }
 
     if (fboLayer) {
         // Detach the texture from the FBO
@@ -541,6 +562,159 @@
     }
 }
 
+void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
+    const Rect& texCoords = layer->texCoords;
+    resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom);
+
+    drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture,
+            layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0],
+            &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap);
+
+    resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+}
+
+void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
+#if RENDER_LAYERS_AS_REGIONS
+    if (layer->region.isRect()) {
+        composeLayerRect(layer, rect);
+        layer->region.clear();
+        return;
+    }
+
+    if (!layer->region.isEmpty()) {
+        size_t count;
+        const android::Rect* rects = layer->region.getArray(&count);
+
+        setupDraw();
+
+        ProgramDescription description;
+        description.hasTexture = true;
+
+        const float alpha = layer->alpha / 255.0f;
+        const bool setColor = description.setColor(alpha, alpha, alpha, alpha);
+        chooseBlending(layer->blend || layer->alpha < 255, layer->mode, description, false);
+
+        useProgram(mCaches.programCache.get(description));
+
+        // Texture
+        bindTexture(layer->texture);
+        glUniform1i(mCaches.currentProgram->getUniform("sampler"), 0);
+
+        // Always premultiplied
+        if (setColor) {
+            mCaches.currentProgram->setColor(alpha, alpha, alpha, alpha);
+        }
+
+        // Mesh
+        int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
+        glEnableVertexAttribArray(texCoordsSlot);
+
+        mModelView.loadIdentity();
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+
+        const float texX = 1.0f / float(layer->width);
+        const float texY = 1.0f / float(layer->height);
+
+        TextureVertex* mesh = mCaches.getRegionMesh();
+        GLsizei numQuads = 0;
+
+        glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
+                gMeshStride, &mesh[0].position[0]);
+        glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE,
+                gMeshStride, &mesh[0].texture[0]);
+
+        for (size_t i = 0; i < count; i++) {
+            const android::Rect* r = &rects[i];
+
+            const float u1 = r->left * texX;
+            const float v1 = (rect.getHeight() - r->top) * texY;
+            const float u2 = r->right * texX;
+            const float v2 = (rect.getHeight() - r->bottom) * texY;
+
+            // TODO: Reject quads outside of the clip
+            TextureVertex::set(mesh++, r->left, r->top, u1, v1);
+            TextureVertex::set(mesh++, r->right, r->top, u2, v1);
+            TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
+            TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
+
+            numQuads++;
+
+            if (numQuads >= REGION_MESH_QUAD_COUNT) {
+                glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, NULL);
+                numQuads = 0;
+                mesh = mCaches.getRegionMesh();
+            }
+        }
+
+        if (numQuads > 0) {
+            glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, NULL);
+        }
+
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+        glDisableVertexAttribArray(texCoordsSlot);
+
+#if DEBUG_LAYERS_AS_REGIONS
+        uint32_t colors[] = {
+                0x7fff0000, 0x7f00ff00,
+                0x7f0000ff, 0x7fff00ff,
+        };
+
+        int offset = 0;
+        int32_t top = rects[0].top;
+        int i = 0;
+
+        for (size_t i = 0; i < count; i++) {
+            if (top != rects[i].top) {
+                offset ^= 0x2;
+                top = rects[i].top;
+            }
+
+            Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
+            drawColorRect(r.left, r.top, r.right, r.bottom, colors[offset + (i & 0x1)],
+                    SkXfermode::kSrcOver_Mode);
+        }
+#endif
+
+        layer->region.clear();
+    }
+#else
+    composeLayerRect(layer, rect);
+#endif
+}
+
+void OpenGLRenderer::dirtyLayer(const float left, const float top,
+        const float right, const float bottom, const mat4 transform) {
+#if RENDER_LAYERS_AS_REGIONS
+    if ((mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region) {
+        Rect bounds(left, top, right, bottom);
+        transform.mapRect(bounds);
+        bounds.intersect(*mSnapshot->clipRect);
+        bounds.snapToPixelBoundaries();
+
+        android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        if (!dirty.isEmpty()) {
+            mSnapshot->region->orSelf(dirty);
+        }
+    }
+#endif
+}
+
+void OpenGLRenderer::dirtyLayer(const float left, const float top,
+        const float right, const float bottom) {
+#if RENDER_LAYERS_AS_REGIONS
+    if ((mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region) {
+        Rect bounds(left, top, right, bottom);
+        bounds.intersect(*mSnapshot->clipRect);
+        bounds.snapToPixelBoundaries();
+
+        android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        if (!dirty.isEmpty()) {
+            mSnapshot->region->orSelf(dirty);
+        }
+    }
+#endif
+}
+
 void OpenGLRenderer::setupDraw() {
     clearLayerRegions();
     if (mDirtyClip) {
@@ -551,21 +725,26 @@
 void OpenGLRenderer::clearLayerRegions() {
     if (mLayers.size() == 0 || mSnapshot->invisible) return;
 
+    Rect clipRect(*mSnapshot->clipRect);
+    clipRect.snapToPixelBoundaries();
+
     for (uint32_t i = 0; i < mLayers.size(); i++) {
         Rect* bounds = mLayers.itemAt(i);
+        if (clipRect.intersects(*bounds)) {
+            // Clear the framebuffer where the layer will draw
+            glScissor(bounds->left, mSnapshot->height - bounds->bottom,
+                    bounds->getWidth(), bounds->getHeight());
+            glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+            glClear(GL_COLOR_BUFFER_BIT);
 
-        // Clear the framebuffer where the layer will draw
-        glScissor(bounds->left, mSnapshot->height - bounds->bottom,
-                bounds->getWidth(), bounds->getHeight());
-        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-        glClear(GL_COLOR_BUFFER_BIT);
+            // Restore the clip
+            dirtyClip();
+        }
 
         delete bounds;
     }
-    mLayers.clear();
 
-    // Restore the clip
-    dirtyClip();
+    mLayers.clear();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -678,7 +857,12 @@
     if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
-    drawTextureRect(r.left, r.top, r.right, r.bottom, texture, paint);
+    // This could be done in a cheaper way, all we need is pass the matrix
+    // to the vertex shader. The save/restore is a bit overkill.
+    save(SkCanvas::kMatrix_SaveFlag);
+    concatMatrix(matrix);
+    drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint);
+    restore();
 }
 
 void OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
@@ -738,11 +922,21 @@
             right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors);
 
     if (mesh) {
-        // Specify right and bottom as +1.0f from left/top to prevent scaling since the
-        // patch mesh already defines the final size
-        drawTextureMesh(left, top, left + 1.0f, top + 1.0f, texture->id, alpha / 255.0f,
+        // Mark the current layer dirty where we are going to draw the patch
+        if ((mSnapshot->flags & Snapshot::kFlagFboTarget) &&
+                mSnapshot->region && mesh->hasEmptyQuads) {
+            const size_t count = mesh->quads.size();
+            for (size_t i = 0; i < count; i++) {
+                Rect bounds = mesh->quads.itemAt(i);
+                dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom,
+                        *mSnapshot->transform);
+            }
+        }
+
+        drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f,
                 mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
-                GL_TRIANGLES, mesh->verticesCount, false, false, mesh->meshBuffer);
+                GL_TRIANGLES, mesh->verticesCount, false, false, mesh->meshBuffer,
+                true, !mesh->hasEmptyQuads);
     }
 }
 
@@ -801,6 +995,7 @@
             mModelView.scale(length, strokeWidth, 1.0f);
         }
         mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+        // TODO: Add bounds to the layer's region
 
         if (mShader) {
             mShader->updateTransforms(mCaches.currentProgram, mModelView, *mSnapshot);
@@ -904,13 +1099,34 @@
 
     // Assume that the modelView matrix does not force scales, rotates, etc.
     const bool linearFilter = mSnapshot->transform->changesBounds();
+
+    // Dimensions are set to (0,0), the layer (if any) won't be dirtied
     setupTextureAlpha8(fontRenderer.getTexture(linearFilter), 0, 0, textureUnit,
             x, y, r, g, b, a, mode, false, true, NULL, NULL);
 
     const Rect& clip = mSnapshot->getLocalClip();
+    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
+
+#if RENDER_LAYERS_AS_REGIONS
+    bool hasLayer = (mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region;
+#else
+    bool hasLayer = false;
+#endif
 
     mCaches.unbindMeshBuffer();
-    fontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
+    if (fontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y,
+            hasLayer ? &bounds : NULL)) {
+#if RENDER_LAYERS_AS_REGIONS
+        if (hasLayer) {
+            mSnapshot->transform->mapRect(bounds);
+            bounds.intersect(*mSnapshot->clipRect);
+            bounds.snapToPixelBoundaries();
+
+            android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
+            mSnapshot->region->orSelf(dirty);
+        }
+#endif
+    }
 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
     glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
@@ -1081,7 +1297,12 @@
      } else {
          mModelView.loadIdentity();
      }
+
      mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+     if (width > 0 && height > 0) {
+         dirtyLayer(x, y, x + width, y + height, *mSnapshot->transform);
+     }
+
      if (setColor) {
          mCaches.currentProgram->setColor(r, g, b, a);
      }
@@ -1214,9 +1435,11 @@
     mModelView.scale(right - left, bottom - top, 1.0f);
     if (!ignoreTransform) {
         mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+        dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
     } else {
         mat4 identity;
         mCaches.currentProgram->set(mOrthoMatrix, mModelView, identity);
+        dirtyLayer(left, top, right, bottom);
     }
     mCaches.currentProgram->setColor(r, g, b, a);
 
@@ -1251,7 +1474,7 @@
 void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
         GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
         GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
-        bool swapSrcDst, bool ignoreTransform, GLuint vbo) {
+        bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) {
     setupDraw();
 
     ProgramDescription description;
@@ -1262,16 +1485,20 @@
     }
 
     mModelView.loadTranslate(left, top, 0.0f);
-    mModelView.scale(right - left, bottom - top, 1.0f);
+    if (!ignoreScale) {
+        mModelView.scale(right - left, bottom - top, 1.0f);
+    }
 
     chooseBlending(blend || alpha < 1.0f, mode, description, swapSrcDst);
 
     useProgram(mCaches.programCache.get(description));
     if (!ignoreTransform) {
         mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+        if (dirty) dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
     } else {
-        mat4 m;
-        mCaches.currentProgram->set(mOrthoMatrix, mModelView, m);
+        mat4 identity;
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, identity);
+        if (dirty) dirtyLayer(left, top, right, bottom);
     }
 
     // Texture
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 3492d2c..2d612d4 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_OPENGL_RENDERER_H
-#define ANDROID_UI_OPENGL_RENDERER_H
+#ifndef ANDROID_HWUI_OPENGL_RENDERER_H
+#define ANDROID_HWUI_OPENGL_RENDERER_H
 
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
@@ -50,6 +50,10 @@
 // Debug
 #define DEBUG_OPENGL 1
 
+// If turned on, layers drawn inside FBOs are optimized with regions
+#define RENDER_LAYERS_AS_REGIONS 0
+#define DEBUG_LAYERS_AS_REGIONS 0
+
 ///////////////////////////////////////////////////////////////////////////////
 // Renderer
 ///////////////////////////////////////////////////////////////////////////////
@@ -176,6 +180,34 @@
             int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo);
 
     /**
+     * Creates a new layer stored in the specified snapshot as an FBO.
+     *
+     * @param layer The layer to store as an FBO
+     * @param snapshot The snapshot associated with the new layer
+     * @param bounds The bounds of the layer
+     * @param previousFbo The name of the current framebuffer
+     */
+    bool createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot,
+            GLuint previousFbo);
+
+    /**
+     * Compose the specified layer as a region.
+     *
+     * @param layer The layer to compose
+     * @param rect The layer's bounds
+     */
+    void composeLayerRegion(Layer* layer, const Rect& rect);
+
+    /**
+     * Compose the specified layer as a simple rectangle.
+     *
+     * @param layer The layer to compose
+     * @param rect The layer's bounds
+     * @param swap If true, the source and destination are swapped
+     */
+    void composeLayerRect(Layer* layer, const Rect& rect, bool swap = false);
+
+    /**
      * Clears all the regions corresponding to the current list of layers.
      * This method MUST be invoked before any drawing operation.
      */
@@ -192,7 +224,7 @@
      * @param color The rectangle's ARGB color, defined as a packed 32 bits word
      * @param mode The Skia xfermode to use
      * @param ignoreTransform True if the current transform should be ignored
-     * @paran ignoreBlending True if the blending is set by the caller
+     * @param ignoreBlending True if the blending is set by the caller
      */
     void drawColorRect(float left, float top, float right, float bottom,
             int color, SkXfermode::Mode mode, bool ignoreTransform = false);
@@ -252,11 +284,14 @@
      * @param swapSrcDst Whether or not the src and dst blending operations should be swapped
      * @param ignoreTransform True if the current transform should be ignored
      * @param vbo The VBO used to draw the mesh
+     * @param ignoreScale True if the model view matrix should not be scaled
+     * @param dirty True if calling this method should dirty the current layer
      */
     void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture,
             float alpha, SkXfermode::Mode mode, bool blend,
             GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
-            bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0);
+            bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0,
+            bool ignoreScale = false, bool dirty = true);
 
     /**
      * Prepares the renderer to draw the specified shadow. The active texture
@@ -411,6 +446,18 @@
         mDirtyClip = true;
     }
 
+    /**
+     * Mark the layer as dirty at the specified coordinates. The coordinates
+     * are transformed with the supplied matrix.
+     */
+    void dirtyLayer(const float left, const float top, const float right, const float bottom,
+            const mat4 transform);
+
+    /**
+     * Mark the layer as dirty at the specified coordinates.
+     */
+    void dirtyLayer(const float left, const float top, const float right, const float bottom);
+
     // Dimensions of the drawing surface
     int mWidth, mHeight;
 
@@ -462,4 +509,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_OPENGL_RENDERER_H
+#endif // ANDROID_HWUI_OPENGL_RENDERER_H
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 3d21431..9b2d476 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -34,6 +34,7 @@
     // 2 triangles per patch, 3 vertices per triangle
     verticesCount = ((xCount + 1) * (yCount + 1) - emptyQuads) * 2 * 3;
     mVertices = new TextureVertex[verticesCount];
+    hasEmptyQuads = emptyQuads > 0;
 
     glGenBuffers(1, &meshBuffer);
 }
@@ -51,6 +52,8 @@
         float left, float top, float right, float bottom,
         const int32_t* xDivs, const int32_t* yDivs,
         const uint32_t width, const uint32_t height, const uint32_t colorKey) {
+    if (hasEmptyQuads) quads.clear();
+
     const uint32_t xStretchCount = (width + 1) >> 1;
     const uint32_t yStretchCount = (height + 1) >> 1;
 
@@ -118,7 +121,7 @@
                 mVertices, GL_STATIC_DRAW);
 }
 
-inline void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2,
+void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2,
         const int32_t xDivs[], uint32_t xCount, float stretchX, float width, float bitmapWidth,
         uint32_t& quadCount, const uint32_t colorKey) {
     float previousStepX = 0.0f;
@@ -150,12 +153,17 @@
     generateQuad(vertex, x1, y1, width, y2, u1, v1, 1.0f, v2, quadCount, colorKey);
 }
 
-inline void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2,
+void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2,
             float u1, float v1, float u2, float v2, uint32_t& quadCount, const uint32_t colorKey) {
     if (((colorKey >> quadCount++) & 0x1) == 1) {
         return;
     }
 
+    if (hasEmptyQuads) {
+        Rect bounds(x1, y1, x2, y2);
+        quads.add(bounds);
+    }
+
     // Left triangle
     TextureVertex::set(vertex++, x1, y1, u1, v1);
     TextureVertex::set(vertex++, x2, y1, u2, v1);
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index ce898937..1e78b2f 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_PATCH_H
-#define ANDROID_UI_PATCH_H
+#ifndef ANDROID_HWUI_PATCH_H
+#define ANDROID_HWUI_PATCH_H
 
 #include <sys/types.h>
 
 #include <GLES2/gl2.h>
 
+#include <utils/Vector.h>
+
+#include "Rect.h"
 #include "Vertex.h"
 #include "utils/Compare.h"
 
@@ -96,15 +99,17 @@
 
     GLuint meshBuffer;
     uint32_t verticesCount;
+    Vector<Rect> quads;
+    bool hasEmptyQuads;
 
 private:
     TextureVertex* mVertices;
 
-    static inline void generateRow(TextureVertex*& vertex, float y1, float y2,
+    void generateRow(TextureVertex*& vertex, float y1, float y2,
             float v1, float v2, const int32_t xDivs[], uint32_t xCount,
             float stretchX, float width, float bitmapWidth,
             uint32_t& quadCount, const uint32_t colorKey);
-    static inline void generateQuad(TextureVertex*& vertex,
+    void generateQuad(TextureVertex*& vertex,
             float x1, float y1, float x2, float y2,
             float u1, float v1, float u2, float v2,
             uint32_t& quadCount, const uint32_t colorKey);
@@ -113,4 +118,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_PATCH_H
+#endif // ANDROID_HWUI_PATCH_H
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index f4eeb09..deba40d 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_PATCH_CACHE_H
-#define ANDROID_UI_PATCH_CACHE_H
+#ifndef ANDROID_HWUI_PATCH_CACHE_H
+#define ANDROID_HWUI_PATCH_CACHE_H
 
 #include <utils/KeyedVector.h>
 
@@ -62,4 +62,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_PATCH_CACHE_H
+#endif // ANDROID_HWUI_PATCH_CACHE_H
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 9a5fc45..db5ce08 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_PATH_CACHE_H
-#define ANDROID_UI_PATH_CACHE_H
+#ifndef ANDROID_HWUI_PATH_CACHE_H
+#define ANDROID_HWUI_PATH_CACHE_H
 
 #include <SkBitmap.h>
 #include <SkPaint.h>
@@ -170,4 +170,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_PATH_CACHE_H
+#endif // ANDROID_HWUI_PATH_CACHE_H
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 25674c6..baed5fd7 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -22,12 +22,6 @@
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
-// Shaders
-///////////////////////////////////////////////////////////////////////////////
-
-#define SHADER_SOURCE(name, source) const char* name = #source
-
-///////////////////////////////////////////////////////////////////////////////
 // Base program
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 026097c..5981662 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_PROGRAM_H
-#define ANDROID_UI_PROGRAM_H
+#ifndef ANDROID_HWUI_PROGRAM_H
+#define ANDROID_HWUI_PROGRAM_H
 
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
@@ -131,4 +131,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_PROGRAM_H
+#endif // ANDROID_HWUI_PROGRAM_H
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 9cb13b3..186e869 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_PROGRAM_CACHE_H
-#define ANDROID_UI_PROGRAM_CACHE_H
+#ifndef ANDROID_HWUI_PROGRAM_CACHE_H
+#define ANDROID_HWUI_PROGRAM_CACHE_H
 
 #include <utils/KeyedVector.h>
 #include <utils/Log.h>
@@ -258,4 +258,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_PROGRAM_CACHE_H
+#endif // ANDROID_HWUI_PROGRAM_CACHE_H
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index db3cb4d..813392b 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_PROPERTIES_H
-#define ANDROID_UI_PROPERTIES_H
+#ifndef ANDROID_HWUI_PROPERTIES_H
+#define ANDROID_HWUI_PROPERTIES_H
 
 #include <cutils/properties.h>
 
@@ -45,15 +45,15 @@
 #define MB(s) s * 1024 * 1024
 
 #define DEFAULT_TEXTURE_CACHE_SIZE 20.0f
-#define DEFAULT_LAYER_CACHE_SIZE 6.0f
+#define DEFAULT_LAYER_CACHE_SIZE 8.0f
 #define DEFAULT_PATH_CACHE_SIZE 4.0f
 #define DEFAULT_PATCH_CACHE_SIZE 512
 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
 #define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
-#define DEFAULT_FBO_CACHE_SIZE 25
+#define DEFAULT_FBO_CACHE_SIZE 12
 
 #define DEFAULT_TEXT_GAMMA 1.4f
 #define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64
 #define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192
 
-#endif // ANDROID_UI_PROPERTIES_H
+#endif // ANDROID_HWUI_PROPERTIES_H
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 8f3655c..71951b7 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_RECT_H
-#define ANDROID_UI_RECT_H
+#ifndef ANDROID_HWUI_RECT_H
+#define ANDROID_HWUI_RECT_H
+
+#include <cmath>
 
 #include <utils/Log.h>
 
@@ -32,25 +34,35 @@
     float right;
     float bottom;
 
-    Rect():
+    // Used by Region
+    typedef float value_type;
+
+    inline Rect():
             left(0),
             top(0),
             right(0),
             bottom(0) {
     }
 
-    Rect(float left, float top, float right, float bottom):
+    inline Rect(float left, float top, float right, float bottom):
             left(left),
             top(top),
             right(right),
             bottom(bottom) {
     }
 
-    Rect(const Rect& r) {
+    inline Rect(float width, float height):
+            left(0.0f),
+            top(0.0f),
+            right(width),
+            bottom(height) {
+    }
+
+    inline Rect(const Rect& r) {
         set(r);
     }
 
-    Rect(Rect& r) {
+    inline Rect(Rect& r) {
         set(r);
     }
 
@@ -72,22 +84,26 @@
         return memcmp(&a, &b, sizeof(a));
     }
 
-    bool isEmpty() const {
+    inline void clear() {
+        left = top = right = bottom = 0.0f;
+    }
+
+    inline bool isEmpty() const {
         return left >= right || top >= bottom;
     }
 
-    void setEmpty() {
-        memset(this, 0, sizeof(*this));
+    inline void setEmpty() {
+        left = top = right = bottom = 0.0f;
     }
 
-    void set(float left, float top, float right, float bottom) {
+    inline void set(float left, float top, float right, float bottom) {
         this->left = left;
         this->right = right;
         this->top = top;
         this->bottom = bottom;
     }
 
-    void set(const Rect& r) {
+    inline void set(const Rect& r) {
         set(r.left, r.top, r.right, r.bottom);
     }
 
@@ -148,6 +164,13 @@
         return false;
     }
 
+    void translate(float dx, float dy) {
+        left += dx;
+        right += dx;
+        top += dy;
+        bottom += dy;
+    }
+
     void snapToPixelBoundaries() {
         left = floorf(left + 0.5f);
         top = floorf(top + 0.5f);
@@ -164,4 +187,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_RECT_H
+#endif // ANDROID_HWUI_RECT_H
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index d9b3718..2256fd1 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_RESOURCE_CACHE_H
-#define ANDROID_UI_RESOURCE_CACHE_H
+#ifndef ANDROID_HWUI_RESOURCE_CACHE_H
+#define ANDROID_HWUI_RESOURCE_CACHE_H
 
 #include <SkBitmap.h>
 #include <SkiaColorFilter.h>
@@ -75,4 +75,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_RESOURCE_CACHE_H
+#endif // ANDROID_HWUI_RESOURCE_CACHE_H
diff --git a/libs/hwui/SkiaColorFilter.h b/libs/hwui/SkiaColorFilter.h
index 17f49f9..bf45e13 100644
--- a/libs/hwui/SkiaColorFilter.h
+++ b/libs/hwui/SkiaColorFilter.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_SKIA_COLOR_FILTER_H
-#define ANDROID_UI_SKIA_COLOR_FILTER_H
+#ifndef ANDROID_HWUI_SKIA_COLOR_FILTER_H
+#define ANDROID_HWUI_SKIA_COLOR_FILTER_H
 
 #include <GLES2/gl2.h>
 #include <SkColorFilter.h>
@@ -123,4 +123,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_SKIA_COLOR_FILTER_H
+#endif // ANDROID_HWUI_SKIA_COLOR_FILTER_H
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index 4cd1b8b..1d884ab 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_SKIA_SHADER_H
-#define ANDROID_UI_SKIA_SHADER_H
+#ifndef ANDROID_HWUI_SKIA_SHADER_H
+#define ANDROID_HWUI_SKIA_SHADER_H
 
 #include <SkShader.h>
 #include <SkXfermode.h>
@@ -216,4 +216,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_SKIA_SHADER_H
+#endif // ANDROID_HWUI_SKIA_SHADER_H
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 2da1950..9f78063 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_SNAPSHOT_H
-#define ANDROID_UI_SNAPSHOT_H
+#ifndef ANDROID_HWUI_SNAPSHOT_H
+#define ANDROID_HWUI_SNAPSHOT_H
 
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
 #include <utils/RefBase.h>
+#include <ui/Region.h>
 
 #include <SkCanvas.h>
-#include <SkRegion.h>
 
 #include "Layer.h"
 #include "Matrix.h"
@@ -46,6 +46,7 @@
     Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), invisible(false) {
         transform = &mTransformRoot;
         clipRect = &mClipRectRoot;
+        region = NULL;
     }
 
     /**
@@ -75,6 +76,13 @@
         } else {
             flags |= Snapshot::kFlagDirtyLocalClip;
         }
+
+        if (s->flags & Snapshot::kFlagFboTarget) {
+            flags |= Snapshot::kFlagFboTarget;
+            region = s->region;
+        } else {
+            region = NULL;
+        }
     }
 
     /**
@@ -105,6 +113,11 @@
          * Indicates that this snapshot has changed the ortho matrix.
          */
         kFlagDirtyOrtho = 0x10,
+        /**
+         * Indicates that this snapshot or an ancestor snapshot is
+         * an FBO layer.
+         */
+        kFlagFboTarget = 0x20
     };
 
     /**
@@ -243,6 +256,11 @@
      */
     Rect* clipRect;
 
+    /**
+     * The ancestor layer's dirty region..
+     */
+    Region* region;
+
 private:
     mat4 mTransformRoot;
     Rect mClipRectRoot;
@@ -253,4 +271,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_SNAPSHOT_H
+#endif // ANDROID_HWUI_SNAPSHOT_H
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index 16e2814..adf09e2 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H
-#define ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H
+#ifndef ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H
+#define ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H
 
 #include <GLES2/gl2.h>
 
@@ -148,4 +148,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_TEXT_DROP_SHADOW_CACHE_H
+#endif // ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index 755074d..4922bb3 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_TEXTURE_H
-#define ANDROID_UI_TEXTURE_H
+#ifndef ANDROID_HWUI_TEXTURE_H
+#define ANDROID_HWUI_TEXTURE_H
 
 #include <GLES2/gl2.h>
 
@@ -86,4 +86,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_TEXTURE_H
+#endif // ANDROID_HWUI_TEXTURE_H
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index d860953..0c7948f 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -200,9 +200,13 @@
         texture->blend = !bitmap->isOpaque();
         break;
     case SkBitmap::kIndex8_Config:
-        uploadPalettedTexture(resize, bitmap, texture->width, texture->height);
+        uploadLoFiTexture(resize, bitmap, texture->width, texture->height);
         texture->blend = false;
         break;
+    case SkBitmap::kARGB_4444_Config:
+        uploadLoFiTexture(resize, bitmap, texture->width, texture->height);
+        texture->blend = true;
+        break;
     default:
         LOGW("Unsupported bitmap config: %d", bitmap->getConfig());
         break;
@@ -215,7 +219,7 @@
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 }
 
-void TextureCache::uploadPalettedTexture(bool resize, SkBitmap* bitmap,
+void TextureCache::uploadLoFiTexture(bool resize, SkBitmap* bitmap,
         uint32_t width, uint32_t height) {
     SkBitmap rgbaBitmap;
     rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 6718597..d9d2387 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_TEXTURE_CACHE_H
-#define ANDROID_UI_TEXTURE_CACHE_H
+#ifndef ANDROID_HWUI_TEXTURE_CACHE_H
+#define ANDROID_HWUI_TEXTURE_CACHE_H
 
 #include <SkBitmap.h>
 
@@ -93,7 +93,7 @@
      */
     void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false);
 
-    void uploadPalettedTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height);
+    void uploadLoFiTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height);
     void uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
             GLenum type, const GLvoid * data);
 
@@ -115,4 +115,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_TEXTURE_CACHE_H
+#endif // ANDROID_HWUI_TEXTURE_CACHE_H
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index 1f54086..bbf4d4a 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_VERTEX_H
-#define ANDROID_UI_VERTEX_H
+#ifndef ANDROID_HWUI_VERTEX_H
+#define ANDROID_HWUI_VERTEX_H
 
 namespace android {
 namespace uirenderer {
@@ -43,4 +43,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_VERTEX_H
+#endif // ANDROID_HWUI_VERTEX_H
diff --git a/libs/hwui/utils/Compare.h b/libs/hwui/utils/Compare.h
index 5ea0fc9..6531e78 100644
--- a/libs/hwui/utils/Compare.h
+++ b/libs/hwui/utils/Compare.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_COMPARE_H
-#define ANDROID_UI_COMPARE_H
+#ifndef ANDROID_HWUI_COMPARE_H
+#define ANDROID_HWUI_COMPARE_H
 
 #include <cmath>
 
@@ -37,4 +37,4 @@
     if (a < rhs.a) return true; \
     if (a == rhs.a)
 
-#endif // ANDROID_UI_COMPARE_H
+#endif // ANDROID_HWUI_COMPARE_H
diff --git a/libs/hwui/utils/GenerationCache.h b/libs/hwui/utils/GenerationCache.h
index 5cea30f..2e76236 100644
--- a/libs/hwui/utils/GenerationCache.h
+++ b/libs/hwui/utils/GenerationCache.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_GENERATION_CACHE_H
-#define ANDROID_UI_GENERATION_CACHE_H
+#ifndef ANDROID_HWUI_GENERATION_CACHE_H
+#define ANDROID_HWUI_GENERATION_CACHE_H
 
 #include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
@@ -242,4 +242,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_GENERATION_CACHE_H
+#endif // ANDROID_HWUI_GENERATION_CACHE_H
diff --git a/libs/hwui/utils/SortedList.h b/libs/hwui/utils/SortedList.h
index 68f5e9d..2fa890a 100644
--- a/libs/hwui/utils/SortedList.h
+++ b/libs/hwui/utils/SortedList.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_SORTED_LIST_H
-#define ANDROID_UI_SORTED_LIST_H
+#ifndef ANDROID_HWUI_SORTED_LIST_H
+#define ANDROID_HWUI_SORTED_LIST_H
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -239,4 +239,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_SORTED_LIST_H
+#endif // ANDROID_HWUI_SORTED_LIST_H
diff --git a/libs/hwui/utils/SortedListImpl.h b/libs/hwui/utils/SortedListImpl.h
index 7da09ef..dc385b5 100644
--- a/libs/hwui/utils/SortedListImpl.h
+++ b/libs/hwui/utils/SortedListImpl.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_SORTED_LIST_IMPL_H
-#define ANDROID_UI_SORTED_LIST_IMPL_H
+#ifndef ANDROID_HWUI_SORTED_LIST_IMPL_H
+#define ANDROID_HWUI_SORTED_LIST_IMPL_H
 
 #include <utils/VectorImpl.h>
 
@@ -62,4 +62,4 @@
 }; // namespace uirenderer
 }; // namespace android
 
-#endif // ANDROID_UI_SORTED_LIST_IMPL_H
+#endif // ANDROID_HWUI_SORTED_LIST_IMPL_H
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 12db908..1994f6a 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -289,7 +289,7 @@
     void flushSpan() {
         bool merge = false;
         if (tail-head == ssize_t(span.size())) {
-            Rect const* p = cur;
+            Rect const* p = span.editArray();
             Rect const* q = head;
             if (p->top == q->bottom) {
                 merge = true;
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 8c3efff..3f6b7a2 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -384,7 +384,7 @@
     private MyMediaScannerClient mClient = new MyMediaScannerClient();
 
     private boolean isDrmEnabled() {
-        String prop = System.getProperty("drm.service.enabled");
+        String prop = SystemProperties.get("drm.service.enabled");
         return prop != null && prop.equals("true");
     }
     
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 2008215..55d69f0 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -423,7 +423,12 @@
      */
     public boolean pingSupplicant() {
         enforceAccessPermission();
-        return mWifiStateMachine.syncPingSupplicant();
+        if (mChannel != null) {
+            return mWifiStateMachine.syncPingSupplicant(mChannel);
+        } else {
+            Slog.e(TAG, "mChannel is not initialized");
+            return false;
+        }
     }
 
     /**
@@ -634,7 +639,12 @@
      */
     public int addOrUpdateNetwork(WifiConfiguration config) {
         enforceChangePermission();
-        return mWifiStateMachine.syncAddOrUpdateNetwork(config);
+        if (mChannel != null) {
+            return mWifiStateMachine.syncAddOrUpdateNetwork(mChannel, config);
+        } else {
+            Slog.e(TAG, "mChannel is not initialized");
+            return -1;
+        }
     }
 
      /**
@@ -662,7 +672,12 @@
      */
     public boolean enableNetwork(int netId, boolean disableOthers) {
         enforceChangePermission();
-        return mWifiStateMachine.syncEnableNetwork(netId, disableOthers);
+        if (mChannel != null) {
+            return mWifiStateMachine.syncEnableNetwork(mChannel, netId, disableOthers);
+        } else {
+            Slog.e(TAG, "mChannel is not initialized");
+            return false;
+        }
     }
 
     /**
@@ -673,7 +688,12 @@
      */
     public boolean disableNetwork(int netId) {
         enforceChangePermission();
-        return mWifiStateMachine.syncDisableNetwork(netId);
+        if (mChannel != null) {
+            return mWifiStateMachine.syncDisableNetwork(mChannel, netId);
+        } else {
+            Slog.e(TAG, "mChannel is not initialized");
+            return false;
+        }
     }
 
     /**
@@ -708,7 +728,12 @@
     public boolean saveConfiguration() {
         boolean result = true;
         enforceChangePermission();
-        return mWifiStateMachine.syncSaveConfig();
+        if (mChannel != null) {
+            return mWifiStateMachine.syncSaveConfig(mChannel);
+        } else {
+            Slog.e(TAG, "mChannel is not initialized");
+            return false;
+        }
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 6bd67cc..3531749 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -279,18 +279,10 @@
     private static final int CMD_ENABLE_RSSI_POLL                 = 82;
     /* RSSI poll */
     private static final int CMD_RSSI_POLL                        = 83;
-    /* Get current RSSI */
-    private static final int CMD_GET_RSSI                         = 84;
-    /* Get approx current RSSI */
-    private static final int CMD_GET_RSSI_APPROX                  = 85;
-    /* Get link speed on connection */
-    private static final int CMD_GET_LINK_SPEED                   = 86;
-    /* Radio mac address */
-    private static final int CMD_GET_MAC_ADDR                     = 87;
     /* Set up packet filtering */
-    private static final int CMD_START_PACKET_FILTERING           = 88;
+    private static final int CMD_START_PACKET_FILTERING           = 84;
     /* Clear packet filter */
-    private static final int CMD_STOP_PACKET_FILTERING            = 89;
+    private static final int CMD_STOP_PACKET_FILTERING            = 85;
     /* Connect to a specified network (network id
      * or WifiConfiguration) This involves increasing
      * the priority of the network, enabling the network
@@ -299,21 +291,23 @@
      * an existing network. All the networks get enabled
      * upon a successful connection or a failure.
      */
-    private static final int CMD_CONNECT_NETWORK                  = 90;
+    private static final int CMD_CONNECT_NETWORK                  = 86;
     /* Save the specified network. This involves adding
      * an enabled network (if new) and updating the
      * config and issuing a save on supplicant config.
      */
-    private static final int CMD_SAVE_NETWORK                     = 91;
+    private static final int CMD_SAVE_NETWORK                     = 87;
     /* Delete the specified network. This involves
      * removing the network and issuing a save on
      * supplicant config.
      */
-    private static final int CMD_FORGET_NETWORK                   = 92;
+    private static final int CMD_FORGET_NETWORK                   = 88;
     /* Start Wi-Fi protected setup push button configuration */
-    private static final int CMD_START_WPS_PBC                    = 93;
-    /* Start Wi-Fi protected setup pin method configuration */
-    private static final int CMD_START_WPS_PIN                    = 94;
+    private static final int CMD_START_WPS_PBC                    = 89;
+    /* Start Wi-Fi protected setup pin method configuration with pin obtained from AP */
+    private static final int CMD_START_WPS_PIN_FROM_AP            = 90;
+    /* Start Wi-Fi protected setup pin method configuration with pin obtained from device */
+    private static final int CMD_START_WPS_PIN_FROM_DEVICE        = 91;
     /**
      * Interval in milliseconds between polling for connection
      * status items that are not sent via asynchronous events.
@@ -334,6 +328,9 @@
     /* 2.4GHz allows 802.11B/G operation */
     private static final int BAND_2G = 2;
 
+    private static final int SUCCESS = 1;
+    private static final int FAILURE = -1;
+
     /**
      * The maximum number of times we will retry a connection to an access point
      * for which we have failed in acquiring an IP address from DHCP. A value of
@@ -519,8 +516,11 @@
     /**
      * TODO: doc
      */
-    public boolean syncPingSupplicant() {
-        return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue;
+    public boolean syncPingSupplicant(AsyncChannel channel) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
+        boolean result = (resultMsg.arg1 != FAILURE);
+        resultMsg.recycle();
+        return result;
     }
 
     /**
@@ -698,8 +698,11 @@
      *
      * @return network id of the new network
      */
-    public int syncAddOrUpdateNetwork(WifiConfiguration config) {
-        return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue;
+    public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
+        int result = resultMsg.arg1;
+        resultMsg.recycle();
+        return result;
     }
 
     public List<WifiConfiguration> syncGetConfiguredNetworks() {
@@ -713,39 +716,24 @@
      */
     public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
         Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
-        boolean result = resultMsg.arg1 != 0;
+        boolean result = (resultMsg.arg1 != FAILURE);
         resultMsg.recycle();
         return result;
     }
 
     /**
-     * Return the result of a removeNetwork
-     *
-     * @param srcMsg is the original message
-     * @param result is true if successfully removed
-     */
-    private void removeNetworkReply(Message srcMsg, boolean result) {
-        mReplyChannel.replyToMessage(srcMsg, CMD_REMOVE_NETWORK, result ? 1 : 0);
-    }
-
-    private class EnableNetParams {
-        private int netId;
-        private boolean disableOthers;
-        EnableNetParams(int n, boolean b) {
-            netId = n;
-            disableOthers = b;
-        }
-    }
-    /**
      * Enable a network
      *
      * @param netId network id of the network
      * @param disableOthers true, if all other networks have to be disabled
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
-    public boolean syncEnableNetwork(int netId, boolean disableOthers) {
-        return sendSyncMessage(CMD_ENABLE_NETWORK,
-                new EnableNetParams(netId, disableOthers)).boolValue;
+    public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId,
+                disableOthers ? 1 : 0);
+        boolean result = (resultMsg.arg1 != FAILURE);
+        resultMsg.recycle();
+        return result;
     }
 
     /**
@@ -754,8 +742,11 @@
      * @param netId network id of the network
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
-    public boolean syncDisableNetwork(int netId) {
-        return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue;
+    public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_DISABLE_NETWORK, netId);
+        boolean result = (resultMsg.arg1 != FAILURE);
+        resultMsg.recycle();
+        return result;
     }
 
     /**
@@ -801,11 +792,11 @@
     }
 
     public void startWpsWithPinFromAccessPoint(String bssid, int apPin) {
-        sendMessage(obtainMessage(CMD_START_WPS_PIN, apPin, 0, bssid));
+        sendMessage(obtainMessage(CMD_START_WPS_PIN_FROM_AP, apPin, 0, bssid));
     }
 
     public int syncStartWpsWithPinFromDevice(AsyncChannel channel, String bssid) {
-        Message resultMsg = channel.sendMessageSynchronously(CMD_START_WPS_PIN, bssid);
+        Message resultMsg = channel.sendMessageSynchronously(CMD_START_WPS_PIN_FROM_DEVICE, bssid);
         int result = resultMsg.arg1;
         resultMsg.recycle();
         return result;
@@ -814,41 +805,6 @@
     public void enableRssiPolling(boolean enabled) {
        sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
     }
-    /**
-     * Get RSSI to currently connected network
-     *
-     * @return RSSI value, -1 on failure
-     */
-    public int syncGetRssi() {
-        return sendSyncMessage(CMD_GET_RSSI).intValue;
-    }
-
-    /**
-     * Get approx RSSI to currently connected network
-     *
-     * @return RSSI value, -1 on failure
-     */
-    public int syncGetRssiApprox() {
-        return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue;
-    }
-
-    /**
-     * Get link speed to currently connected network
-     *
-     * @return link speed, -1 on failure
-     */
-    public int syncGetLinkSpeed() {
-        return sendSyncMessage(CMD_GET_LINK_SPEED).intValue;
-    }
-
-    /**
-     * Get MAC address of radio
-     *
-     * @return MAC address, null on failure
-     */
-    public String syncGetMacAddress() {
-        return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue;
-    }
 
     /**
      * Start packet filtering
@@ -943,8 +899,11 @@
      *
      * TODO: deprecate this
      */
-    public boolean syncSaveConfig() {
-        return sendSyncMessage(CMD_SAVE_CONFIG).boolValue;
+    public boolean syncSaveConfig(AsyncChannel channel) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG);
+        boolean result = (resultMsg.arg1 != FAILURE);
+        resultMsg.recycle();
+        return result;
     }
 
     /**
@@ -1018,73 +977,6 @@
      * Internal private functions
      ********************************************************/
 
-    class SyncReturn {
-        boolean boolValue;
-        int intValue;
-        String stringValue;
-        Object objValue;
-    }
-
-    class SyncParams {
-        Object mParameter;
-        SyncReturn mSyncReturn;
-        SyncParams() {
-            mSyncReturn = new SyncReturn();
-        }
-        SyncParams(Object p) {
-            mParameter = p;
-            mSyncReturn = new SyncReturn();
-        }
-    }
-
-    /**
-     * message.obj is used to store SyncParams
-     */
-    private SyncReturn syncedSend(Message msg) {
-        SyncParams syncParams = (SyncParams) msg.obj;
-        synchronized(syncParams) {
-            if (DBG) Log.d(TAG, "syncedSend " + msg);
-            sendMessage(msg);
-            try {
-                syncParams.wait();
-            } catch (InterruptedException e) {
-                Log.e(TAG, "sendSyncMessage: unexpected interruption of wait()");
-                return null;
-            }
-        }
-        return syncParams.mSyncReturn;
-    }
-
-    private SyncReturn sendSyncMessage(Message msg) {
-        SyncParams syncParams = new SyncParams();
-        msg.obj = syncParams;
-        return syncedSend(msg);
-    }
-
-    private SyncReturn sendSyncMessage(int what, Object param) {
-        SyncParams syncParams = new SyncParams(param);
-        Message msg = obtainMessage(what, syncParams);
-        return syncedSend(msg);
-    }
-
-
-    private SyncReturn sendSyncMessage(int what) {
-        return sendSyncMessage(obtainMessage(what));
-    }
-
-    private void notifyOnMsgObject(Message msg) {
-        SyncParams syncParams = (SyncParams) msg.obj;
-        if (syncParams != null) {
-            synchronized(syncParams) {
-                if (DBG) Log.d(TAG, "notifyOnMsgObject " + msg);
-                syncParams.notify();
-            }
-        }
-        else {
-            Log.e(TAG, "Error! syncParams in notifyOnMsgObject is null");
-        }
-    }
-
     private void setWifiState(int wifiState) {
         final int previousWifiState = mWifiState.get();
 
@@ -1620,7 +1512,6 @@
         @Override
         public boolean processMessage(Message message) {
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
-            SyncParams syncParams;
             switch (message.what) {
                 case CMD_SET_BLUETOOTH_HEADSET_PROXY:
                     mBluetoothHeadset = (BluetoothHeadset) message.obj;
@@ -1633,19 +1524,10 @@
                 case CMD_ENABLE_NETWORK:
                 case CMD_DISABLE_NETWORK:
                 case CMD_ADD_OR_UPDATE_NETWORK:
-                case CMD_GET_RSSI:
-                case CMD_GET_RSSI_APPROX:
-                case CMD_GET_LINK_SPEED:
-                case CMD_GET_MAC_ADDR:
-                case CMD_SAVE_CONFIG:
-                    syncParams = (SyncParams) message.obj;
-                    syncParams.mSyncReturn.boolValue = false;
-                    syncParams.mSyncReturn.intValue = -1;
-                    syncParams.mSyncReturn.stringValue = null;
-                    notifyOnMsgObject(message);
-                    break;
                 case CMD_REMOVE_NETWORK:
-                    removeNetworkReply(message, false);
+                case CMD_SAVE_CONFIG:
+                case CMD_START_WPS_PIN_FROM_DEVICE:
+                    mReplyChannel.replyToMessage(message, message.what, FAILURE);
                     break;
                 case CMD_ENABLE_RSSI_POLL:
                     mEnableRssiPolling = (message.arg1 == 1);
@@ -1687,7 +1569,7 @@
                 case CMD_SAVE_NETWORK:
                 case CMD_FORGET_NETWORK:
                 case CMD_START_WPS_PBC:
-                case CMD_START_WPS_PIN:
+                case CMD_START_WPS_PIN_FROM_AP:
                     break;
                 default:
                     Log.e(TAG, "Error! unhandled message" + message);
@@ -2032,7 +1914,6 @@
         @Override
         public boolean processMessage(Message message) {
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
-            SyncParams syncParams;
             WifiConfiguration config;
             switch(message.what) {
                 case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
@@ -2060,36 +1941,29 @@
                     sendScanResultsAvailableBroadcast();
                     break;
                 case CMD_PING_SUPPLICANT:
-                    syncParams = (SyncParams) message.obj;
-                    syncParams.mSyncReturn.boolValue = WifiNative.pingCommand();
-                    notifyOnMsgObject(message);
+                    boolean ok = WifiNative.pingCommand();
+                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
                     break;
                 case CMD_ADD_OR_UPDATE_NETWORK:
                     EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-                    syncParams = (SyncParams) message.obj;
-                    config = (WifiConfiguration) syncParams.mParameter;
-                    syncParams.mSyncReturn.intValue = WifiConfigStore.addOrUpdateNetwork(config);
-                    notifyOnMsgObject(message);
+                    config = (WifiConfiguration) message.obj;
+                    mReplyChannel.replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
+                            WifiConfigStore.addOrUpdateNetwork(config));
                     break;
                 case CMD_REMOVE_NETWORK:
                     EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-                    boolean ok = WifiConfigStore.removeNetwork(message.arg1);
-                    removeNetworkReply(message, ok);
+                    ok = WifiConfigStore.removeNetwork(message.arg1);
+                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
                     break;
                 case CMD_ENABLE_NETWORK:
                     EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-                    syncParams = (SyncParams) message.obj;
-                    EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter;
-                    syncParams.mSyncReturn.boolValue = WifiConfigStore.enableNetwork(
-                            enableNetParams.netId, enableNetParams.disableOthers);
-                    notifyOnMsgObject(message);
+                    ok = WifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
+                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
                     break;
                 case CMD_DISABLE_NETWORK:
                     EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-                    syncParams = (SyncParams) message.obj;
-                    syncParams.mSyncReturn.boolValue = WifiConfigStore.disableNetwork(
-                            message.arg1);
-                    notifyOnMsgObject(message);
+                    ok = WifiConfigStore.disableNetwork(message.arg1);
+                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
                     break;
                 case CMD_BLACKLIST_NETWORK:
                     EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
@@ -2099,9 +1973,9 @@
                     WifiNative.clearBlacklistCommand();
                     break;
                 case CMD_SAVE_CONFIG:
-                    syncParams = (SyncParams) message.obj;
-                    syncParams.mSyncReturn.boolValue = WifiConfigStore.saveConfig();
-                    notifyOnMsgObject(message);
+                    ok = WifiConfigStore.saveConfig();
+                    mReplyChannel.replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
+
                     // Inform the backup manager about a data change
                     IBackupManager ibm = IBackupManager.Stub.asInterface(
                             ServiceManager.getService(Context.BACKUP_SERVICE));
@@ -2113,11 +1987,6 @@
                         }
                     }
                     break;
-                case CMD_GET_MAC_ADDR:
-                    syncParams = (SyncParams) message.obj;
-                    syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand();
-                    notifyOnMsgObject(message);
-                    break;
                     /* Cannot start soft AP while in client mode */
                 case CMD_START_AP:
                     Log.d(TAG, "Failed to start soft AP with a running supplicant");
@@ -2207,7 +2076,6 @@
         @Override
         public boolean processMessage(Message message) {
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
-            SyncParams syncParams;
             switch(message.what) {
                 case CMD_SET_SCAN_TYPE:
                     if (message.arg1 == SCAN_ACTIVE) {
@@ -2333,7 +2201,6 @@
         @Override
         public boolean processMessage(Message message) {
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
-            SyncParams syncParams;
             switch(message.what) {
                 case CMD_SET_SCAN_MODE:
                     if (message.arg1 == SCAN_ONLY_MODE) {
@@ -2374,7 +2241,6 @@
         @Override
         public boolean processMessage(Message message) {
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
-            SyncParams syncParams;
             StateChangeResult stateChangeResult;
             switch(message.what) {
                 case PASSWORD_MAY_BE_INCORRECT_EVENT:
@@ -2449,18 +2315,25 @@
                         transitionTo(mDisconnectingState);
                     }
                     break;
-                case CMD_START_WPS_PIN:
+                case CMD_START_WPS_PIN_FROM_AP:
                     bssid = (String) message.obj;
                     int apPin = message.arg1;
-                    int pin;
-                    if (apPin != 0) {
-                        /* WPS pin from access point */
-                        success = WifiConfigStore.startWpsWithPinFromAccessPoint(bssid, apPin);
-                    } else {
-                        pin = WifiConfigStore.startWpsWithPinFromDevice(bssid);
-                        success = (pin != -1);
-                        mReplyChannel.replyToMessage(message, CMD_START_WPS_PIN, pin);
+
+                    /* WPS pin from access point */
+                    success = WifiConfigStore.startWpsWithPinFromAccessPoint(bssid, apPin);
+
+                    if (success) {
+                        mWpsStarted = true;
+                        /* Expect a disconnection from the old connection */
+                        transitionTo(mDisconnectingState);
                     }
+                    break;
+                case CMD_START_WPS_PIN_FROM_DEVICE:
+                    bssid = (String) message.obj;
+                    int pin = WifiConfigStore.startWpsWithPinFromDevice(bssid);
+                    success = (pin != FAILURE);
+                    mReplyChannel.replyToMessage(message, CMD_START_WPS_PIN_FROM_DEVICE, pin);
+
                     if (success) {
                         mWpsStarted = true;
                         /* Expect a disconnection from the old connection */
@@ -2491,21 +2364,6 @@
                     handleNetworkDisconnect();
                     transitionTo(mDisconnectedState);
                     break;
-                case CMD_GET_RSSI:
-                    syncParams = (SyncParams) message.obj;
-                    syncParams.mSyncReturn.intValue = WifiNative.getRssiCommand();
-                    notifyOnMsgObject(message);
-                    break;
-                case CMD_GET_RSSI_APPROX:
-                    syncParams = (SyncParams) message.obj;
-                    syncParams.mSyncReturn.intValue = WifiNative.getRssiApproxCommand();
-                    notifyOnMsgObject(message);
-                    break;
-                case CMD_GET_LINK_SPEED:
-                    syncParams = (SyncParams) message.obj;
-                    syncParams.mSyncReturn.intValue = WifiNative.getLinkSpeedCommand();
-                    notifyOnMsgObject(message);
-                    break;
                 default:
                     return NOT_HANDLED;
             }