Merge changes Ib8b18f1b,I0d799d82,Ia6222322

* changes:
  Always give ContentResolver a valid Context.
  Define failed connections column, and reset.
  Add method to get process group.
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 0d45bbc..2b4260d 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -173,17 +173,6 @@
     public static final String DEBUG_SHOW_OVERDRAW_PROPERTY = "debug.hwui.show_overdraw";
 
     /**
-     * Turn on to allow region clipping (see
-     * {@link android.graphics.Canvas#clipPath(android.graphics.Path)} and
-     * {@link android.graphics.Canvas#clipRegion(android.graphics.Region)}.
-     *
-     * When this option is turned on a stencil buffer is always required.
-     * If this option is off a stencil buffer is only created when the overdraw
-     * debugging mode is turned on.
-     */
-    private static final boolean REGION_CLIPPING_ENABLED = false;
-
-    /**
      * A process can set this flag to false to prevent the use of hardware
      * rendering.
      * 
@@ -885,15 +874,6 @@
             if (value != mShowOverdraw) {
                 changed = true;
                 mShowOverdraw = value;
-
-                if (!REGION_CLIPPING_ENABLED) {
-                    if (surface != null && isEnabled()) {
-                        if (validate()) {
-                            sEglConfig = loadEglConfig();
-                            invalidate(surface);
-                        }
-                    }
-                }
             }
 
             if (nLoadProperties()) {
@@ -1764,9 +1744,8 @@
 
         @Override
         int[] getConfig(boolean dirtyRegions) {
-            //noinspection PointlessBooleanExpression
-            final int stencilSize = mShowOverdraw || REGION_CLIPPING_ENABLED ?
-                    GLES20Canvas.getStencilSize() : 0;
+            //noinspection PointlessBooleanExpression,ConstantConditions
+            final int stencilSize = GLES20Canvas.getStencilSize();
             final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
 
             return new int[] {
diff --git a/core/java/com/android/internal/os/SamplingProfilerIntegration.java b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
index df0fcd9..6429aa4 100644
--- a/core/java/com/android/internal/os/SamplingProfilerIntegration.java
+++ b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
@@ -106,7 +106,7 @@
         }
 
         ThreadGroup group = Thread.currentThread().getThreadGroup();
-        SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group);
+        SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupThreadSet(group);
         samplingProfiler = new SamplingProfiler(samplingProfilerDepth, threadSet);
         samplingProfiler.start(samplingProfilerMilliseconds);
         startMillis = System.currentTimeMillis();
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 5b8200e..9fa0a06 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -316,7 +316,7 @@
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"የባትሪ ስታስቲክስን ይቀይራል"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"የተሰበሰቡ የባትሪ  ስታስቲክሶችን እንዲቀይር ለመተግበሪያው ያስችለዋል። ለመደበኛ መተግበሪያዎች ጥቅም አይደለም።"</string>
     <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"የመተግበሪያ ክወናዎች ስታቲስቲክስን ይቀይሩ"</string>
-    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"መተግበሪያው የተሰበሰቡ የክወና ስታስቲክሶችን እንዲቀይር ይፈቅድለታል። ለመደበኛ መተግበሪያዎች ጥቅም ያልሆነ።"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"መተግበሪያው የተሰበሰቡ የክወና ስታስቲክሶችን እንዲቀይር ይፈቅድሎታል። ለመደበኛ መተግበሪያዎች ጥቅም ያልሆነ።"</string>
     <string name="permlab_backup" msgid="470013022865453920">"የስርዓት መጠባበቂያን ተቆጣጠር እናእነበረበት መልስ"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"የስርዓቱን ምትኬ  እና እንደነበር መልስ መንገዶችን ለመቆጣጠር ለመተግበሪያው ይፈቅዳሉ፡፡ በመደበኛ መተግበሪያዎች ለመጠቀም አይሆንም፡፡"</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"የሙሉ መጠበቂያ ወይም እነበረበት መልስ ከዋኝ አረጋግጥ"</string>
@@ -943,7 +943,7 @@
   </plurals>
   <plurals name="in_num_hours">
     <item quantity="one" msgid="7164353342477769999">"በ  1 ሰዓት"</item>
-    <item quantity="other" msgid="547290677353727389">"በ <xliff:g id="COUNT">%d</xliff:g> ሰዓታት"</item>
+    <item quantity="other" msgid="547290677353727389">"በ <xliff:g id="COUNT">%d</xliff:g> ሰዓቶች"</item>
   </plurals>
   <plurals name="in_num_days">
     <item quantity="one" msgid="5413088743009839518">"ነገ"</item>
@@ -975,7 +975,7 @@
   </plurals>
   <plurals name="abbrev_in_num_hours">
     <item quantity="one" msgid="3274708118124045246">"በ  1 ሰዓት"</item>
-    <item quantity="other" msgid="3705373766798013406">"በ <xliff:g id="COUNT">%d</xliff:g> ሰዓታት"</item>
+    <item quantity="other" msgid="3705373766798013406">"በ <xliff:g id="COUNT">%d</xliff:g> ሰዓቶች"</item>
   </plurals>
   <plurals name="abbrev_in_num_days">
     <item quantity="one" msgid="2178576254385739855">"ነገ"</item>
@@ -1006,7 +1006,7 @@
   </plurals>
   <plurals name="duration_hours">
     <item quantity="one" msgid="8917467491248809972">"1 ሰዓት"</item>
-    <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> ሰዓታት"</item>
+    <item quantity="other" msgid="3863962854246773930">"<xliff:g id="COUNT">%d</xliff:g> ሰዓቶች"</item>
   </plurals>
     <string name="VideoView_error_title" msgid="3534509135438353077">"የቪዲዮ ችግር"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"ይቅርታ፣ ይህ ቪዲዮ በዚህ መሣሪያ ለመልቀቅ ትክክል አይደለም።"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 590dfee..8fc1450 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -315,8 +315,8 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Permite a una aplicación leer los datos actuales de uso de batería de bajo nivel. Puede permitir a la aplicación buscar información detallada sobre las aplicaciones que usas."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modificar las estadísticas de la batería"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Permite a la aplicación modificar las estadísticas recopiladas de la batería. Las aplicaciones normales no deben utilizarlo."</string>
-    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"Modificar estadísticas de operaciones de aplicación"</string>
-    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permite que la aplicación modifique las estadísticas recopiladas de operación de la aplicación. Las aplicaciones normales no deben utilizar este permiso."</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modificar estadísticas de uso de aplicaciones"</string>
+    <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Permite que la aplicación modifique las estadísticas recopiladas sobre el uso de aplicaciones. Las aplicaciones normales no deben utilizar este permiso."</string>
     <string name="permlab_backup" msgid="470013022865453920">"copia de seguridad y restauración del sistema de control"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Permite que la aplicación controle el mecanismo de copia de seguridad y restauración del sistema. Las aplicaciones normales no deben utilizar este permiso."</string>
     <string name="permlab_confirm_full_backup" msgid="5557071325804469102">"Confirmar una copia completa de seguridad o una operación de restauración"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 7d18dc9..d3eaa61 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -315,7 +315,7 @@
     <string name="permdesc_batteryStats" msgid="5897346582882915114">"Consente a un\'applicazione di leggere i dati di utilizzo della batteria di basso livello correnti. Potrebbe consentire all\'applicazione di trovare informazioni dettagliate sulle applicazioni che utilizzi."</string>
     <string name="permlab_updateBatteryStats" msgid="3719689764536379557">"modifica statistiche batteria"</string>
     <string name="permdesc_updateBatteryStats" msgid="6862817857178025002">"Consente all\'applicazione di modificare le statistiche raccolte sulla batteria. Da non usare per normali applicazioni."</string>
-    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modifica statistiche funzion. app"</string>
+    <string name="permlab_updateAppOpsStats" msgid="8829097373851521505">"modifica statistiche funzionamento app"</string>
     <string name="permdesc_updateAppOpsStats" msgid="50784596594403483">"Consente all\'applicazione di modificare le statistiche raccolte sul funzionamento dell\'applicazione. Da non usare per normali applicazioni."</string>
     <string name="permlab_backup" msgid="470013022865453920">"controllo del backup di sistema e ripristino"</string>
     <string name="permdesc_backup" msgid="6912230525140589891">"Consente all\'applicazione di controllare il meccanismo di backup e di ripristino del sistema. Da non usare per normali applicazioni."</string>
diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd
index c12761b..57fa498 100644
--- a/docs/html/develop/index.jd
+++ b/docs/html/develop/index.jd
@@ -51,7 +51,7 @@
               <li class="item carousel-home">
                  <div class="col-8">
                    <img
-src="http://4.bp.blogspot.com/-lfjzgG5Dqrk/UHMThRtpRwI/AAAAAAAABpk/h4d3nsmkgPM/s400/mint.png"
+src="//lh4.ggpht.com/-lfjzgG5Dqrk/UHMThRtpRwI/AAAAAAAABpk/h4d3nsmkgPM/s400/mint.png"
 class="play no-shadow no-transform" />
                  </div>
                 <div class="content-right col-6">
@@ -65,7 +65,7 @@
                <li class="item carousel-home">
                    <div class="col-8">
                      <img
-src="http://1.bp.blogspot.com/-6K1kfNOdek8/T72bXvtTSQI/AAAAAAAABmw/kYzmJt0_328/s1600/google-play-subscriptions.png" class="play"></div>
+src="//lh4.ggpht.com/-6K1kfNOdek8/T72bXvtTSQI/AAAAAAAABmw/kYzmJt0_328/s1600/google-play-subscriptions.png" class="play"></div>
                    <div class="content-right col-6">
                    <h2>In-app Subscriptions with Trials</h2>
                    <p>You can now set up a <strong>free trial period</strong> for any Google Play in-app subscription, making it easy for users try your subscriber content before automatically converting to a full subscription. Free trials give you a new way to bring users into your products and engage them effectively. </p>
@@ -77,7 +77,7 @@
                <li class="item carousel-home">
                    <div class="col-8">
                      <img
-src="http://2.bp.blogspot.com/-MgN5DnoO5XU/UHYGYzTcAOI/AAAAAAAABs4/jTS7sKkfBcM/s1600/pubsites.png" class="play"></div>
+src="//lh4.ggpht.com/-MgN5DnoO5XU/UHYGYzTcAOI/AAAAAAAABs4/jTS7sKkfBcM/s1600/pubsites.png" class="play"></div>
                    <div class="content-right col-6">
                    <p class="title-intro">From the blog:</p>
                    <h2>New Google Play Developer Console</h2>
@@ -90,7 +90,7 @@
                <li class="item carousel-home">
                  <div class="col-8">
                    <img
-src="http://4.bp.blogspot.com/-g05If_eKKRQ/UAcrVLI-OYI/AAAAAAAAAr8/AWvunVb5S-w/s320/nexus7.png"
+src="//lh4.ggpht.com/-g05If_eKKRQ/UAcrVLI-OYI/AAAAAAAAAr8/AWvunVb5S-w/s320/nexus7.png"
 class="play no-shadow no-transform" />
                  </div>
                 <div class="content-right col-6">
@@ -127,7 +127,7 @@
               <p>You can take advantage of the auth APIs in Google Play services to let your back end know which app is calling and for which user....</p>
               </a></li>
             <li><a href="//android-developers.blogspot.com/2012/12/daydream-interactive-screen-savers.html">
-              <div class="feed-image" style="background:url('//3.bp.blogspot.com/-wVsUOo4xGE0/UNy9mZ1nmMI/AAAAAAAAB4w/f6rhyLn5KbI/s1600/daydream-example.jpg') no-repeat 0 0;background-position:right top;"></div>
+              <div class="feed-image" style="background:url('//lh4.ggpht.com/-wVsUOo4xGE0/UNy9mZ1nmMI/AAAAAAAAB4w/f6rhyLn5KbI/s1600/daydream-example.jpg') no-repeat 0 0;background-position:right top;"></div>
               <h4>Daydream: Interactive Screen Savers</h4>
               <p>Daydream is an interactive screen-saver mode introduced in Android 4.2. Learn how to add Daydreams to your apps...</p>
               </a></li>
diff --git a/docs/html/index.jd b/docs/html/index.jd
index cf06324..afda7a9 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -22,7 +22,7 @@
                         <script type="text/javascript">
                             var params = { allowScriptAccess: "always" };
                             var atts = { id: "ytapiplayer" };
-                            swfobject.embedSWF("http://www.youtube.com/v/RRelFvc6Czo?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1",
+                            swfobject.embedSWF("//www.youtube.com/v/RRelFvc6Czo?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1",
                               "ytapiplayer", "600", "338", "8", null, null, params, atts);
 
                             // Callback used to pause/resume carousel based on video state
diff --git a/docs/html/training/articles/smp.jd b/docs/html/training/articles/smp.jd
index 53d7879..d46787d 100644
--- a/docs/html/training/articles/smp.jd
+++ b/docs/html/training/articles/smp.jd
@@ -628,7 +628,7 @@
 
 <p>The “loop_until” seen in previous examples has been expanded to show the load
 of B into reg0.  reg1 is assigned the numeric value 8, and reg2 is loaded from
-the address [A+reg1] (same location that thread 1 is accessing).</p>
+the address [A+reg1] (the same location that thread 1 is accessing).</p>
 
 <p>This will not behave correctly because the load from B could be observed
 after the load from [A+reg1].  We can fix this with a load/load barrier after
@@ -640,7 +640,7 @@
 <th>Thread 2</th>
 </tr>
 <tr>
-<td><code>A = 41<br />
+<td><code>[A+8] = 41<br />
 <em>store/store barrier</em><br />
 B = 1    // “A is ready”</code></td>
 <td><code>loop:<br />
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 628d8a0..ae188be 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -270,9 +270,7 @@
     GammaFontRenderer* fontRenderer;
 
     Dither dither;
-#if STENCIL_BUFFER_SIZE
     Stencil stencil;
-#endif
 
     // Debug methods
     PFNGLINSERTEVENTMARKEREXTPROC eventMark;
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index ee1d391..1d85b70 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -41,6 +41,7 @@
     renderer = NULL;
     displayList = NULL;
     fbo = 0;
+    stencil = 0;
     debugDrawUpdate = false;
     Caches::getInstance().resourceCache.incrementRefcount(this);
 }
@@ -53,9 +54,22 @@
     deleteTexture();
 }
 
-void Layer::removeFbo() {
+void Layer::removeFbo(bool flush) {
+    if (stencil) {
+        // TODO: recycle & cache instead of simply deleting
+        GLuint previousFbo;
+        glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
+        if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
+        if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+
+        glDeleteRenderbuffers(1, &stencil);
+        stencil = 0;
+    }
+
     if (fbo) {
-        LayerRenderer::flushLayer(this);
+        if (flush) LayerRenderer::flushLayer(this);
+        // If put fails the cache will delete the FBO
         Caches::getInstance().fboCache.put(fbo);
         fbo = 0;
     }
@@ -75,7 +89,5 @@
     }
 }
 
-
-
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 181eb6c..9ef4894 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -48,7 +48,12 @@
     Layer(const uint32_t layerWidth, const uint32_t layerHeight);
     ~Layer();
 
-    void removeFbo();
+    /**
+     * Calling this method will remove (either by recycling or
+     * destroying) the associated FBO, if present, and any render
+     * buffer (stencil for instance.)
+     */
+    void removeFbo(bool flush = true);
 
     /**
      * Sets this layer's region to a rectangle. Computes the appropriate
@@ -134,6 +139,14 @@
         return fbo;
     }
 
+    inline void setStencilRenderBuffer(GLuint renderBuffer) {
+        this->stencil = renderBuffer;
+    }
+
+    inline GLuint getStencilRenderBuffer() {
+        return stencil;
+    }
+
     inline GLuint getTexture() {
         return texture.id;
     }
@@ -212,10 +225,6 @@
         texture.id = 0;
     }
 
-    inline void deleteFbo() {
-        if (fbo) glDeleteFramebuffers(1, &fbo);
-    }
-
     inline void allocateTexture(GLenum format, GLenum storage) {
 #if DEBUG_LAYERS
         ALOGD("  Allocate layer: %dx%d", getWidth(), getHeight());
@@ -275,6 +284,12 @@
     GLuint fbo;
 
     /**
+     * Name of the render buffer used as the stencil buffer. If the
+     * name is 0, this layer does not have a stencil buffer.
+     */
+    GLuint stencil;
+
+    /**
      * Indicates whether this layer has been used already.
      */
     bool empty;
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 3484d41..ba59bb39 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -100,13 +100,21 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// Dirty region tracking
+// Layer support
 ///////////////////////////////////////////////////////////////////////////////
 
 bool LayerRenderer::hasLayer() {
     return true;
 }
 
+void LayerRenderer::ensureStencilBuffer() {
+    attachStencilBufferToLayer(mLayer);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Dirty region tracking
+///////////////////////////////////////////////////////////////////////////////
+
 Region* LayerRenderer::getRegion() {
     if (getSnapshot()->flags & Snapshot::kFlagFboTarget) {
         return OpenGLRenderer::getRegion();
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index c44abce..7a8bdc5 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -64,6 +64,7 @@
     static void flushLayer(Layer* layer);
 
 protected:
+    virtual void ensureStencilBuffer();
     virtual bool hasLayer();
     virtual Region* getRegion();
     virtual GLint getTargetFbo();
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index a924362..79fae2b 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -30,6 +30,16 @@
 namespace android {
 namespace uirenderer {
 
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+static const float EPSILON = 0.0000001f;
+
+///////////////////////////////////////////////////////////////////////////////
+// Matrix
+///////////////////////////////////////////////////////////////////////////////
+
 void Matrix4::loadIdentity() {
     data[kScaleX]       = 1.0f;
     data[kSkewY]        = 0.0f;
@@ -51,44 +61,91 @@
     data[kTranslateZ]   = 0.0f;
     data[kPerspective2] = 1.0f;
 
-    mIsIdentity = true;
-    mSimpleMatrix = true;
+    mType = kTypeIdentity | kTypeRectToRect;
+}
+
+static bool isZero(float f) {
+    return fabs(f) <= EPSILON;
+}
+
+uint32_t Matrix4::getType() const {
+    if (mType & kTypeUnknown) {
+        mType = kTypeIdentity;
+
+        if (data[kPerspective0] != 0.0f || data[kPerspective1] != 0.0f ||
+                data[kPerspective2] != 1.0f) {
+            mType |= kTypePerspective;
+        }
+
+        if (data[kTranslateX] != 0.0f || data[kTranslateY] != 0.0f) {
+            mType |= kTypeTranslate;
+        }
+
+        float m00 = data[kScaleX];
+        float m01 = data[kSkewX];
+        float m10 = data[kSkewY];
+        float m11 = data[kScaleY];
+
+        if (m01 != 0.0f || m10 != 0.0f) {
+            mType |= kTypeAffine;
+        }
+
+        if (m00 != 1.0f || m11 != 1.0f) {
+            mType |= kTypeScale;
+        }
+
+        // The following section determines whether the matrix will preserve
+        // rectangles. For instance, a rectangle transformed by a pure
+        // translation matrix will result in a rectangle. A rectangle
+        // transformed by a 45 degrees rotation matrix is not a rectangle.
+        // If the matrix has a perspective component then we already know
+        // it doesn't preserve rectangles.
+        if (!(mType & kTypePerspective)) {
+            if ((isZero(m00) && isZero(m11) && !isZero(m01) && !isZero(m10)) ||
+                    (isZero(m01) && isZero(m10) && !isZero(m00) && !isZero(m11))) {
+                mType |= kTypeRectToRect;
+            }
+        }
+    }
+    return mType;
+}
+
+uint32_t Matrix4::getGeometryType() const {
+    return getType() & sGeometryMask;
+}
+
+bool Matrix4::rectToRect() const {
+    return getType() & kTypeRectToRect;
 }
 
 bool Matrix4::changesBounds() const {
-    return !(data[0] == 1.0f && data[1] == 0.0f && data[2] == 0.0f && data[4] == 0.0f &&
-             data[5] == 1.0f && data[6] == 0.0f && data[8] == 0.0f && data[9] == 0.0f &&
-             data[10] == 1.0f);
+    return getType() & (kTypeScale | kTypeAffine | kTypePerspective);
 }
 
 bool Matrix4::isPureTranslate() const {
-    return mSimpleMatrix && data[kScaleX] == 1.0f && data[kScaleY] == 1.0f;
+    return getGeometryType() == kTypeTranslate;
 }
 
 bool Matrix4::isSimple() const {
-    return mSimpleMatrix;
+    return getGeometryType() <= (kTypeScale | kTypeTranslate);
 }
 
 bool Matrix4::isIdentity() const {
-    return mIsIdentity;
+    return getGeometryType() == kTypeIdentity;
 }
 
 bool Matrix4::isPerspective() const {
-    return data[kPerspective0] != 0.0f || data[kPerspective1] != 0.0f ||
-            data[kPerspective2] != 1.0f;
+    return getType() & kTypePerspective;
 }
 
 void Matrix4::load(const float* v) {
     memcpy(data, v, sizeof(data));
-    // TODO: Do something smarter here
-    mSimpleMatrix = false;
-    mIsIdentity = false;
+    mType = kTypeUnknown;
 }
 
 void Matrix4::load(const Matrix4& v) {
     memcpy(data, v.data, sizeof(data));
-    mSimpleMatrix = v.mSimpleMatrix;
-    mIsIdentity = v.mIsIdentity;
+    mType = v.getType();
 }
 
 void Matrix4::load(const SkMatrix& v) {
@@ -108,8 +165,14 @@
 
     data[kScaleZ] = 1.0f;
 
-    mSimpleMatrix = (v.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask));
-    mIsIdentity = v.isIdentity();
+    // NOTE: The flags are compatible between SkMatrix and this class.
+    //       However, SkMatrix::getType() does not return the flag
+    //       kRectStaysRect. The return value is masked with 0xF
+    //       so we need the extra rectStaysRect() check
+    mType = v.getType();
+    if (v.rectStaysRect()) {
+        mType |= kTypeRectToRect;
+    }
 }
 
 void Matrix4::copyTo(SkMatrix& v) const {
@@ -158,8 +221,7 @@
     data[kPerspective2] = (v.data[kScaleX] * v.data[kScaleY] -
             v.data[kSkewX] * v.data[kSkewY]) * scale;
 
-    mSimpleMatrix = v.mSimpleMatrix;
-    mIsIdentity = v.mIsIdentity;
+    mType = kTypeUnknown;
 }
 
 void Matrix4::copyTo(float* v) const {
@@ -178,7 +240,7 @@
     for (int i = 0; i < 16; i++) {
         data[i] *= v;
     }
-    mIsIdentity = false;
+    mType = kTypeUnknown;
 }
 
 void Matrix4::loadTranslate(float x, float y, float z) {
@@ -188,7 +250,7 @@
     data[kTranslateY] = y;
     data[kTranslateZ] = z;
 
-    mIsIdentity = false;
+    mType = kTypeTranslate | kTypeRectToRect;
 }
 
 void Matrix4::loadScale(float sx, float sy, float sz) {
@@ -198,7 +260,7 @@
     data[kScaleY] = sy;
     data[kScaleZ] = sz;
 
-    mIsIdentity = false;
+    mType = kTypeScale | kTypeRectToRect;
 }
 
 void Matrix4::loadSkew(float sx, float sy) {
@@ -216,8 +278,23 @@
     data[kPerspective1] = 0.0f;
     data[kPerspective2] = 1.0f;
 
-    mSimpleMatrix = false;
-    mIsIdentity = false;
+    mType = kTypeUnknown;
+}
+
+void Matrix4::loadRotate(float angle) {
+    angle *= float(M_PI / 180.0f);
+    float c = cosf(angle);
+    float s = sinf(angle);
+
+    loadIdentity();
+
+    data[kScaleX]     = c;
+    data[kSkewX]      = -s;
+
+    data[kSkewY]      = s;
+    data[kScaleY]     = c;
+
+    mType = kTypeUnknown;
 }
 
 void Matrix4::loadRotate(float angle, float x, float y, float z) {
@@ -257,8 +334,7 @@
     data[6]       =    yz * nc + xs;
     data[kScaleZ] = z * z * nc +  c;
 
-    mSimpleMatrix = false;
-    mIsIdentity = false;
+    mType = kTypeUnknown;
 }
 
 void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) {
@@ -282,8 +358,7 @@
         set(i, 3, w);
     }
 
-    mSimpleMatrix = u.mSimpleMatrix && v.mSimpleMatrix;
-    mIsIdentity = false;
+    mType = kTypeUnknown;
 }
 
 void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) {
@@ -296,13 +371,13 @@
     data[kTranslateY] = -(top + bottom) / (top - bottom);
     data[kTranslateZ] = -(far + near) / (far - near);
 
-    mIsIdentity = false;
+    mType = kTypeTranslate | kTypeScale | kTypeRectToRect;
 }
 
 #define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c)
 
 void Matrix4::mapPoint(float& x, float& y) const {
-    if (mSimpleMatrix) {
+    if (isSimple()) {
         MUL_ADD_STORE(x, data[kScaleX], data[kTranslateX]);
         MUL_ADD_STORE(y, data[kScaleY], data[kTranslateY]);
         return;
@@ -318,7 +393,7 @@
 }
 
 void Matrix4::mapRect(Rect& r) const {
-    if (mSimpleMatrix) {
+    if (isSimple()) {
         MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]);
         MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]);
         MUL_ADD_STORE(r.top, data[kScaleY], data[kTranslateY]);
@@ -376,7 +451,7 @@
 }
 
 void Matrix4::dump() const {
-    ALOGD("Matrix4[simple=%d", mSimpleMatrix);
+    ALOGD("Matrix4[simple=%d, type=0x%x", isSimple(), getType());
     ALOGD("  %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]);
     ALOGD("  %f %f %f %f", data[kSkewY], data[kScaleY], data[9], data[kTranslateY]);
     ALOGD("  %f %f %f %f", data[2], data[6], data[kScaleZ], data[kTranslateZ]);
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index f86823d..46a5597 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -48,6 +48,21 @@
         kPerspective2 = 15
     };
 
+    // NOTE: The flags from kTypeIdentity to kTypePerspective
+    //       must be kept in sync with the type flags found
+    //       in SkMatrix
+    enum Type {
+        kTypeIdentity = 0,
+        kTypeTranslate = 0x1,
+        kTypeScale = 0x2,
+        kTypeAffine = 0x4,
+        kTypePerspective = 0x8,
+        kTypeRectToRect = 0x10,
+        kTypeUnknown = 0x20,
+    };
+
+    static const int sGeometryMask = 0xf;
+
     Matrix4() {
         loadIdentity();
     }
@@ -75,11 +90,14 @@
     void loadTranslate(float x, float y, float z);
     void loadScale(float sx, float sy, float sz);
     void loadSkew(float sx, float sy);
+    void loadRotate(float angle);
     void loadRotate(float angle, float x, float y, float z);
     void loadMultiply(const Matrix4& u, const Matrix4& v);
 
     void loadOrtho(float left, float right, float bottom, float top, float near, float far);
 
+    uint32_t getType() const;
+
     void multiply(const Matrix4& v) {
         Matrix4 u;
         u.loadMultiply(*this, v);
@@ -112,10 +130,14 @@
         multiply(u);
     }
 
-    bool isPureTranslate() const;
+    /**
+     * If the matrix is identity or translate and/or scale.
+     */
     bool isSimple() const;
+    bool isPureTranslate() const;
     bool isIdentity() const;
     bool isPerspective() const;
+    bool rectToRect() const;
 
     bool changesBounds() const;
 
@@ -131,8 +153,7 @@
     void dump() const;
 
 private:
-    bool mSimpleMatrix;
-    bool mIsIdentity;
+    mutable uint32_t mType;
 
     inline float get(int i, int j) const {
         return data[i * 4 + j];
@@ -141,6 +162,9 @@
     inline void set(int i, int j, float v) {
         data[i * 4 + j] = v;
     }
+
+    uint32_t getGeometryType() const;
+
 }; // class Matrix4
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index bb1edbb..be34b40 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -837,6 +837,8 @@
         return;
     }
 
+    Layer* layer = current->layer;
+    const Rect& rect = layer->layer;
     const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer;
 
     if (fboLayer) {
@@ -844,6 +846,9 @@
 
         // Detach the texture from the FBO
         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+
+        layer->removeFbo(false);
+
         // Unbind current FBO and restore previous one
         glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
         debugOverdraw(true, false);
@@ -851,9 +856,6 @@
         startTiling(previous);
     }
 
-    Layer* layer = current->layer;
-    const Rect& rect = layer->layer;
-
     if (!fboLayer && layer->getAlpha() < 255) {
         drawColorRect(rect.left, rect.top, rect.right, rect.bottom,
                 layer->getAlpha() << 24, SkXfermode::kDstIn_Mode, true);
@@ -881,17 +883,6 @@
         composeLayerRect(layer, rect, true);
     }
 
-    if (fboLayer) {
-        // Note: No need to use glDiscardFramebufferEXT() since we never
-        //       create/compose layers that are not on screen with this
-        //       code path
-        // See LayerRenderer::destroyLayer(Layer*)
-
-        // Put the FBO name back in the cache, if it doesn't fit, it will be destroyed
-        mCaches.fboCache.put(current->fbo);
-        layer->setFbo(0);
-    }
-
     dirtyClip();
 
     // Failing to add the layer to the cache should happen only if the layer is too large
@@ -1001,10 +992,14 @@
         const float texY = 1.0f / float(layer->getHeight());
         const float height = rect.getHeight();
 
+        setupDraw();
+
+        // We must get (and therefore bind) the region mesh buffer
+        // after we setup drawing in case we need to mess with the
+        // stencil buffer in setupDraw()
         TextureVertex* mesh = mCaches.getRegionMesh();
         GLsizei numQuads = 0;
 
-        setupDraw();
         setupDrawWithTexture();
         setupDrawColor(alpha, alpha, alpha, alpha);
         setupDrawColorFilter();
@@ -1089,6 +1084,25 @@
 #endif
 }
 
+void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color,
+        SkXfermode::Mode mode, bool dirty) {
+    int count = 0;
+    Vector<float> rects;
+
+    SkRegion::Iterator it(region);
+    while (!it.done()) {
+        const SkIRect& r = it.rect();
+        rects.push(r.fLeft);
+        rects.push(r.fTop);
+        rects.push(r.fRight);
+        rects.push(r.fBottom);
+        count++;
+        it.next();
+    }
+
+    drawColorRects(rects.array(), count, color, mode, true, dirty);
+}
+
 void OpenGLRenderer::dirtyLayer(const float left, const float top,
         const float right, const float bottom, const mat4 transform) {
     if (hasLayer()) {
@@ -1219,6 +1233,65 @@
     }
 }
 
+void OpenGLRenderer::ensureStencilBuffer() {
+    // Thanks to the mismatch between EGL and OpenGL ES FBO we
+    // cannot attach a stencil buffer to fbo0 dynamically. Let's
+    // just hope we have one when hasLayer() returns false.
+    if (hasLayer()) {
+        attachStencilBufferToLayer(mSnapshot->layer);
+    }
+}
+
+void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
+    // The layer's FBO is already bound when we reach this stage
+    if (!layer->getStencilRenderBuffer()) {
+        // TODO: See Layer::removeFbo(). The stencil renderbuffer should be cached
+        GLuint buffer;
+        glGenRenderbuffers(1, &buffer);
+        glBindRenderbuffer(GL_RENDERBUFFER, buffer);
+        glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8,
+                layer->getWidth(), layer->getHeight());
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer);
+        layer->setStencilRenderBuffer(buffer);
+    }
+}
+
+void OpenGLRenderer::setStencilFromClip() {
+    if (!mCaches.debugOverdraw) {
+        if (!mSnapshot->clipRegion->isEmpty()) {
+            // NOTE: The order here is important, we must set dirtyClip to false
+            //       before any draw call to avoid calling back into this method
+            mDirtyClip = false;
+
+            ensureStencilBuffer();
+
+            mCaches.stencil.enableWrite();
+
+            // Clear the stencil but first make sure we restrict drawing
+            // to the region's bounds
+            bool resetScissor = mCaches.enableScissor();
+            if (resetScissor) {
+                // The scissor was not set so we now need to update it
+                setScissorFromClip();
+            }
+            mCaches.stencil.clear();
+            if (resetScissor) mCaches.disableScissor();
+
+            // NOTE: We could use the region contour path to generate a smaller mesh
+            //       Since we are using the stencil we could use the red book path
+            //       drawing technique. It might increase bandwidth usage though.
+
+            // The last parameter is important: we are not drawing in the color buffer
+            // so we don't want to dirty the current layer, if any
+            drawRegionRects(*mSnapshot->clipRegion, 0xff000000, SkXfermode::kSrc_Mode, false);
+
+            mCaches.stencil.enableTest();
+        } else {
+            mCaches.stencil.disable();
+        }
+    }
+}
+
 const Rect& OpenGLRenderer::getClipBounds() {
     return mSnapshot->getLocalClip();
 }
@@ -1284,40 +1357,60 @@
     return rejected;
 }
 
+void OpenGLRenderer::debugClip() {
+#if DEBUG_CLIP_REGIONS
+    if (!isDeferred() && !mSnapshot->clipRegion->isEmpty()) {
+        drawRegionRects(*mSnapshot->clipRegion, 0x7f00ff00, SkXfermode::kSrcOver_Mode);
+    }
+#endif
+}
+
 bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
-    bool clipped = mSnapshot->clip(left, top, right, bottom, op);
+    if (CC_LIKELY(mSnapshot->transform->rectToRect())) {
+        bool clipped = mSnapshot->clip(left, top, right, bottom, op);
+        if (clipped) {
+            dirtyClip();
+        }
+        return !mSnapshot->clipRect->isEmpty();
+    }
+
+    SkPath path;
+    path.addRect(left, top, right, bottom);
+
+    return clipPath(&path, op);
+}
+
+bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) {
+    SkMatrix transform;
+    mSnapshot->transform->copyTo(transform);
+
+    SkPath transformed;
+    path->transform(transform, &transformed);
+
+    SkRegion clip;
+    if (!mSnapshot->clipRegion->isEmpty()) {
+        clip.setRegion(*mSnapshot->clipRegion);
+    } else {
+        Rect* bounds = mSnapshot->clipRect;
+        clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom);
+    }
+
+    SkRegion region;
+    region.setPath(transformed, clip);
+
+    bool clipped = mSnapshot->clipRegionTransformed(region, op);
     if (clipped) {
         dirtyClip();
-#if DEBUG_CLIP_REGIONS
-        if (!isDeferred() && mSnapshot->clipRegion && !mSnapshot->clipRegion->isRect()) {
-            int count = 0;
-            Vector<float> rects;
-            SkRegion::Iterator it(*mSnapshot->clipRegion);
-            while (!it.done()) {
-                const SkIRect& r = it.rect();
-                rects.push(r.fLeft);
-                rects.push(r.fTop);
-                rects.push(r.fRight);
-                rects.push(r.fBottom);
-                count++;
-                it.next();
-            }
-
-            drawColorRects(rects.array(), count, 0x7f00ff00, SkXfermode::kSrcOver_Mode, true);
-        }
-#endif
     }
     return !mSnapshot->clipRect->isEmpty();
 }
 
-bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) {
-    const SkRect& bounds = path->getBounds();
-    return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op);
-}
-
 bool OpenGLRenderer::clipRegion(SkRegion* region, SkRegion::Op op) {
-    const SkIRect& bounds = region->getBounds();
-    return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op);
+    bool clipped = mSnapshot->clipRegionTransformed(*region, op);
+    if (clipped) {
+        dirtyClip();
+    }
+    return !mSnapshot->clipRect->isEmpty();
 }
 
 Rect* OpenGLRenderer::getClipRect() {
@@ -1332,8 +1425,11 @@
     // TODO: It would be best if we could do this before quickReject()
     //       changes the scissor test state
     if (clear) clearLayerRegions();
+    // Make sure setScissor & setStencil happen at the beginning of
+    // this method
     if (mDirtyClip) {
         setScissorFromClip();
+        setStencilFromClip();
     }
     mDescription.reset();
     mSetShaderColor = false;
@@ -3085,7 +3181,7 @@
 }
 
 status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color,
-        SkXfermode::Mode mode, bool ignoreTransform) {
+        SkXfermode::Mode mode, bool ignoreTransform, bool dirty) {
 
     float left = FLT_MAX;
     float top = FLT_MAX;
@@ -3103,7 +3199,7 @@
         float r = rects[index + 2];
         float b = rects[index + 3];
 
-        if (!quickRejectNoScissor(left, top, right, bottom)) {
+        if (ignoreTransform || !quickRejectNoScissor(left, top, right, bottom)) {
             Vertex::set(vertex++, l, b);
             Vertex::set(vertex++, l, t);
             Vertex::set(vertex++, r, t);
@@ -3136,7 +3232,7 @@
     setupDrawColorFilterUniforms();
     setupDrawVertices((GLvoid*) &mesh[0].position[0]);
 
-    if (hasLayer()) {
+    if (dirty && hasLayer()) {
         dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
     }
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index f07325f..d4e1eb5 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -287,6 +287,19 @@
     void resumeAfterLayer();
 
     /**
+     * This method is called whenever a stencil buffer is required. Subclasses
+     * should override this method and call attachStencilBufferToLayer() on the
+     * appropriate layer(s).
+     */
+    virtual void ensureStencilBuffer();
+
+    /**
+     * Obtains a stencil render buffer (allocating it if necessary) and
+     * attaches it to the specified layer.
+     */
+    void attachStencilBufferToLayer(Layer* layer);
+
+    /**
      * Compose the layer defined in the current snapshot with the layer
      * defined by the previous snapshot.
      *
@@ -423,6 +436,12 @@
     void setScissorFromClip();
 
     /**
+     * Sets the clipping region using the stencil buffer. The clip region
+     * is defined by the current snapshot's clipRegion member.
+     */
+    void setStencilFromClip();
+
+    /**
      * Performs a quick reject but does not affect the scissor. Returns
      * the transformed rect to test and the current clip.
      */
@@ -524,9 +543,10 @@
      * @param color The rectangles' 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
+     * @param dirty True if calling this method should dirty the current layer
      */
     status_t drawColorRects(const float* rects, int count, int color,
-            SkXfermode::Mode mode, bool ignoreTransform = false);
+            SkXfermode::Mode mode, bool ignoreTransform = false, bool dirty = true);
 
     /**
      * Draws the shape represented by the specified path texture.
@@ -774,6 +794,19 @@
      */
     void drawRegionRects(const Region& region);
 
+    /**
+     * Renders the specified region as a series of rectangles. The region
+     * must be in screen-space coordinates.
+     */
+    void drawRegionRects(const SkRegion& region, int color, SkXfermode::Mode mode,
+            bool dirty = false);
+
+    /**
+     * Draws the current clip region if any. Only when DEBUG_CLIP_REGIONS
+     * is turned on.
+     */
+    void debugClip();
+
     void debugOverdraw(bool enable, bool clear);
     void renderOverdraw();
 
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index d947299..22c7dde 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -31,7 +31,7 @@
     transform = &mTransformRoot;
     clipRect = &mClipRectRoot;
     region = NULL;
-    clipRegion = NULL;
+    clipRegion = &mClipRegionRoot;
 }
 
 /**
@@ -39,12 +39,10 @@
  * the previous snapshot.
  */
 Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags):
-        flags(0), previous(s), layer(NULL), fbo(s->fbo),
+        flags(0), previous(s), layer(s->layer), fbo(s->fbo),
         invisible(s->invisible), empty(false),
         viewport(s->viewport), height(s->height), alpha(s->alpha) {
 
-    clipRegion = NULL;
-
     if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
         mTransformRoot.load(*s->transform);
         transform = &mTransformRoot;
@@ -55,17 +53,13 @@
     if (saveFlags & SkCanvas::kClip_SaveFlag) {
         mClipRectRoot.set(*s->clipRect);
         clipRect = &mClipRectRoot;
-#if STENCIL_BUFFER_SIZE
-        if (s->clipRegion) {
+        if (!s->clipRegion->isEmpty()) {
             mClipRegionRoot.op(*s->clipRegion, SkRegion::kUnion_Op);
-            clipRegion = &mClipRegionRoot;
         }
-#endif
+        clipRegion = &mClipRegionRoot;
     } else {
         clipRect = s->clipRect;
-#if STENCIL_BUFFER_SIZE
         clipRegion = s->clipRegion;
-#endif
     }
 
     if (s->flags & Snapshot::kFlagFboTarget) {
@@ -81,41 +75,38 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void Snapshot::ensureClipRegion() {
-#if STENCIL_BUFFER_SIZE
-    if (!clipRegion) {
-        clipRegion = &mClipRegionRoot;
+    if (clipRegion->isEmpty()) {
         clipRegion->setRect(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
     }
-#endif
 }
 
 void Snapshot::copyClipRectFromRegion() {
-#if STENCIL_BUFFER_SIZE
     if (!clipRegion->isEmpty()) {
         const SkIRect& bounds = clipRegion->getBounds();
         clipRect->set(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
 
         if (clipRegion->isRect()) {
             clipRegion->setEmpty();
-            clipRegion = NULL;
         }
     } else {
         clipRect->setEmpty();
-        clipRegion = NULL;
     }
-#endif
 }
 
 bool Snapshot::clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op) {
-#if STENCIL_BUFFER_SIZE
     SkIRect tmp;
     tmp.set(left, top, right, bottom);
     clipRegion->op(tmp, op);
     copyClipRectFromRegion();
     return true;
-#else
-    return false;
-#endif
+}
+
+bool Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) {
+    ensureClipRegion();
+    clipRegion->op(region, op);
+    copyClipRectFromRegion();
+    flags |= Snapshot::kFlagClipSet;
+    return true;
 }
 
 bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) {
@@ -129,7 +120,7 @@
 
     switch (op) {
         case SkRegion::kIntersect_Op: {
-            if (CC_UNLIKELY(clipRegion)) {
+            if (CC_UNLIKELY(!clipRegion->isEmpty())) {
                 ensureClipRegion();
                 clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op);
             } else {
@@ -142,7 +133,7 @@
             break;
         }
         case SkRegion::kUnion_Op: {
-            if (CC_UNLIKELY(clipRegion)) {
+            if (CC_UNLIKELY(!clipRegion->isEmpty())) {
                 ensureClipRegion();
                 clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kUnion_Op);
             } else {
@@ -171,12 +162,9 @@
 
 void Snapshot::setClip(float left, float top, float right, float bottom) {
     clipRect->set(left, top, right, bottom);
-#if STENCIL_BUFFER_SIZE
-    if (clipRegion) {
+    if (!clipRegion->isEmpty()) {
         clipRegion->setEmpty();
-        clipRegion = NULL;
     }
-#endif
     flags |= Snapshot::kFlagClipSet;
 }
 
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 9c612ff..ffd4729 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -94,6 +94,12 @@
     bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op);
 
     /**
+     * Modifies the current clip with the specified region and operation.
+     * The specified region is considered already transformed.
+     */
+    bool clipRegionTransformed(const SkRegion& region, SkRegion::Op op);
+
+    /**
      * Sets the current clip.
      */
     void setClip(float left, float top, float right, float bottom);
@@ -136,7 +142,7 @@
     sp<Snapshot> previous;
 
     /**
-     * Only set when the flag kFlagIsLayer is set.
+     * A pointer to the currently active layer.
      *
      * This snapshot does not own the layer, this pointer must not be freed.
      */
@@ -200,8 +206,6 @@
      *
      * This is a reference to a region owned by this snapshot or another
      * snapshot. This pointer must not be freed. See ::mClipRegionRoot.
-     *
-     * This field is used only if STENCIL_BUFFER_SIZE is > 0.
      */
     SkRegion* clipRegion;
 
@@ -234,9 +238,7 @@
     Rect mClipRectRoot;
     Rect mLocalClip;
 
-#if STENCIL_BUFFER_SIZE
     SkRegion mClipRegionRoot;
-#endif
 
 }; // class Snapshot
 
diff --git a/libs/hwui/Stencil.cpp b/libs/hwui/Stencil.cpp
index 84df82b..4fcd51d 100644
--- a/libs/hwui/Stencil.cpp
+++ b/libs/hwui/Stencil.cpp
@@ -37,7 +37,7 @@
 void Stencil::enableTest() {
     if (mState != kTest) {
         enable();
-        glStencilFunc(GL_EQUAL, 0x1, 0x1);
+        glStencilFunc(GL_EQUAL, 0xff, 0xff);
         // We only want to test, let's keep everything
         glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
@@ -48,7 +48,7 @@
 void Stencil::enableWrite() {
     if (mState != kWrite) {
         enable();
-        glStencilFunc(GL_ALWAYS, 0x1, 0x1);
+        glStencilFunc(GL_ALWAYS, 0xff, 0xff);
         // The test always passes so the first two values are meaningless
         glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 8c2dd8e..580bfe7 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -147,7 +147,7 @@
     <string name="accessibility_ringer_silent" msgid="9061243307939135383">"Mode silenci."</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"S\'ha omès <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificació omesa."</string>
-    <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Capa de notificació."</string>
+    <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Àrea de notificacions"</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Configuració ràpida."</string>
     <string name="accessibility_desc_recent_apps" msgid="9014032916410590027">"Aplicacions recents."</string>
     <string name="accessibility_quick_settings_user" msgid="1104846699869476855">"Usuari <xliff:g id="USER">%s</xliff:g>."</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 28c4113..54c4666 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -75,7 +75,8 @@
 
     private Drawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon;
     private Drawable mRecentIcon;
-    
+    private Drawable mRecentLandIcon;
+
     private DelegateViewHelper mDelegateHelper;
     private DeadZone mDeadZone;
 
@@ -179,6 +180,7 @@
         mBackAltIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime);
         mBackAltLandIcon = res.getDrawable(R.drawable.ic_sysbar_back_ime);
         mRecentIcon = res.getDrawable(R.drawable.ic_sysbar_recent);
+        mRecentLandIcon = res.getDrawable(R.drawable.ic_sysbar_recent_land);
     }
 
     @Override
@@ -238,7 +240,7 @@
                 ? (mVertical ? mBackAltLandIcon : mBackAltIcon)
                 : (mVertical ? mBackLandIcon : mBackIcon));
 
-        ((ImageView)getRecentsButton()).setImageDrawable(mRecentIcon);
+        ((ImageView)getRecentsButton()).setImageDrawable(mVertical ? mRecentLandIcon : mRecentIcon);
 
         setDisabledFlags(mDisabledFlags, true);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
index e5ef5fe..6eb88be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
@@ -135,7 +135,7 @@
         mLastPokeTime = event.getEventTime();
         if (DEBUG)
             Slog.v(TAG, "poked! size=" + getSize(mLastPokeTime));
-        postInvalidate();
+        if (mShouldFlash) postInvalidate();
     }
 
     public void setFlash(float f) {
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 7d2ba19..57ce1d6 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -148,7 +148,16 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        
+
+        <activity
+                android:name="ClipRegion2Activity"
+                android:label="_ClipRegion2">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
         <activity
                 android:name="DisplayListLayersActivity"
                 android:label="__DisplayListLayers">
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegion2Activity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegion2Activity.java
new file mode 100644
index 0000000..066e35c
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegion2Activity.java
@@ -0,0 +1,86 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Region;
+import android.os.Bundle;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ClipRegion2Activity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final RegionView group = new RegionView(this);
+
+        final TextView text = new TextView(this);
+        text.setText(buildText());
+        group.addView(text);
+
+        setContentView(group);
+    }
+
+    private static CharSequence buildText() {
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0; i < 10; i++) {
+            buffer.append(LOREM_IPSUM);
+        }
+        return buffer;
+    }
+
+    public static class RegionView extends FrameLayout {
+        private final Region mRegion = new Region();
+        private float mClipPosition = 0.0f;
+
+        public RegionView(Context c) {
+            super(c);
+        }
+
+        public float getClipPosition() {
+            return mClipPosition;
+        }
+
+        public void setClipPosition(float clipPosition) {
+            mClipPosition = clipPosition;
+            invalidate();
+        }
+
+        @Override
+        protected void dispatchDraw(Canvas canvas) {
+
+            canvas.save();
+
+            mRegion.setEmpty();
+            mRegion.op(0, 0, getWidth(), getHeight(),
+                    Region.Op.REPLACE);
+            mRegion.op(getWidth() / 4, getHeight() / 4, 3 * getWidth() / 4, 3 * getHeight() / 4,
+                    Region.Op.DIFFERENCE);
+
+            canvas.clipRegion(mRegion);
+            super.dispatchDraw(canvas);
+
+            canvas.restore();
+        }
+    }
+
+    private static final String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sagittis molestie aliquam. Donec metus metus, laoreet nec sagittis vitae, ultricies sit amet eros. Suspendisse sed massa sit amet felis consectetur gravida. In vitae erat mi, in egestas nisl. Phasellus quis ipsum massa, at scelerisque arcu. Nam lectus est, pellentesque eget lacinia non, congue vitae augue. Aliquam erat volutpat. Pellentesque bibendum tincidunt viverra. Aliquam erat volutpat. Maecenas pretium vulputate placerat. Nulla varius elementum rutrum. Aenean mollis blandit imperdiet. Pellentesque interdum fringilla ligula.";
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
index d5daa5f..3d3d709 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
@@ -16,38 +16,77 @@
 
 package com.android.test.hwui;
 
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Region;
+import android.graphics.Path;
 import android.os.Bundle;
-import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.TextView;
 
 @SuppressWarnings({"UnusedDeclaration"})
 public class ClipRegionActivity extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        final RegionView view = new RegionView(this);
-        setContentView(view);
+
+        final RegionView group = new RegionView(this);
+
+        final TextView text = new TextView(this);
+        text.setText(buildText());
+        group.addView(text);
+
+        setContentView(group);
+
+        ObjectAnimator animator = ObjectAnimator.ofFloat(group, "clipPosition", 0.0f, 1.0f);
+        animator.setDuration(3000);
+        animator.setRepeatCount(ValueAnimator.INFINITE);
+        animator.setRepeatMode(ValueAnimator.REVERSE);
+        animator.start();
     }
 
-    public static class RegionView extends View {
+    private static CharSequence buildText() {
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0; i < 10; i++) {
+            buffer.append(LOREM_IPSUM);
+        }
+        return buffer;
+    }
+
+    public static class RegionView extends FrameLayout {
+        private final Path mClipPath = new Path();
+        private float mClipPosition = 0.0f;
+
         public RegionView(Context c) {
             super(c);
         }
 
-        @Override
-        protected void onDraw(Canvas canvas) {
-            super.onDraw(canvas);
+        public float getClipPosition() {
+            return mClipPosition;
+        }
 
-            canvas.save();
-            canvas.clipRect(100.0f, 100.0f, getWidth() - 100.0f, getHeight() - 100.0f,
-                    Region.Op.DIFFERENCE);
-            canvas.drawARGB(128, 255, 0, 0);
-            canvas.restore();
-
+        public void setClipPosition(float clipPosition) {
+            mClipPosition = clipPosition;
             invalidate();
         }
+
+        @Override
+        protected void dispatchDraw(Canvas canvas) {
+
+            canvas.save();
+
+            mClipPath.reset();
+            mClipPath.addCircle(mClipPosition * getWidth(), getHeight() / 2.0f,
+                    getWidth() / 4.0f, Path.Direction.CW);
+
+            canvas.clipPath(mClipPath);
+            super.dispatchDraw(canvas);
+
+            canvas.restore();
+        }
     }
+
+    private static final String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sagittis molestie aliquam. Donec metus metus, laoreet nec sagittis vitae, ultricies sit amet eros. Suspendisse sed massa sit amet felis consectetur gravida. In vitae erat mi, in egestas nisl. Phasellus quis ipsum massa, at scelerisque arcu. Nam lectus est, pellentesque eget lacinia non, congue vitae augue. Aliquam erat volutpat. Pellentesque bibendum tincidunt viverra. Aliquam erat volutpat. Maecenas pretium vulputate placerat. Nulla varius elementum rutrum. Aenean mollis blandit imperdiet. Pellentesque interdum fringilla ligula.";
 }