Merge "Check whether media recorder client exists before dumping." into ics-mr0
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 4225393..7d683a5 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -990,8 +990,8 @@
                 mBound = false;
                 mContext.unbindService(this);
             }
-            mSyncWakeLock.setWorkSource(null);
             mSyncWakeLock.release();
+            mSyncWakeLock.setWorkSource(null);
         }
 
         @Override
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index e344197..438c536 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1095,6 +1095,16 @@
         }
     }
 
+    private static long computeWakeLock(Timer timer, long batteryRealtime, int which) {
+        if (timer != null) {
+            // Convert from microseconds to milliseconds with rounding
+            long totalTimeMicros = timer.getTotalTimeLocked(batteryRealtime, which);
+            long totalTimeMillis = (totalTimeMicros + 500) / 1000;
+            return totalTimeMillis;
+        }
+        return 0;
+    }
+
     /**
      *
      * @param sb a StringBuilder object.
@@ -1109,9 +1119,7 @@
             long batteryRealtime, String name, int which, String linePrefix) {
         
         if (timer != null) {
-            // Convert from microseconds to milliseconds with rounding
-            long totalTimeMicros = timer.getTotalTimeLocked(batteryRealtime, which);
-            long totalTimeMillis = (totalTimeMicros + 500) / 1000;
+            long totalTimeMillis = computeWakeLock(timer, batteryRealtime, which);
             
             int count = timer.getCountLocked(which);
             if (totalTimeMillis != 0) {
@@ -1735,6 +1743,8 @@
 
             Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
             if (wakelocks.size() > 0) {
+                long totalFull = 0, totalPartial = 0, totalWindow = 0;
+                int count = 0;
                 for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent
                     : wakelocks.entrySet()) {
                     Uid.Wakelock wl = ent.getValue();
@@ -1754,6 +1764,44 @@
                         // Only print out wake locks that were held
                         pw.println(sb.toString());
                         uidActivity = true;
+                        count++;
+                    }
+                    totalFull += computeWakeLock(wl.getWakeTime(WAKE_TYPE_FULL),
+                            batteryRealtime, which);
+                    totalPartial += computeWakeLock(wl.getWakeTime(WAKE_TYPE_PARTIAL),
+                            batteryRealtime, which);
+                    totalWindow += computeWakeLock(wl.getWakeTime(WAKE_TYPE_WINDOW),
+                            batteryRealtime, which);
+                }
+                if (count > 1) {
+                    if (totalFull != 0 || totalPartial != 0 || totalWindow != 0) {
+                        sb.setLength(0);
+                        sb.append(prefix);
+                        sb.append("    TOTAL wake: ");
+                        boolean needComma = false;
+                        if (totalFull != 0) {
+                            needComma = true;
+                            formatTimeMs(sb, totalFull);
+                            sb.append("full");
+                        }
+                        if (totalPartial != 0) {
+                            if (needComma) {
+                                sb.append(", ");
+                            }
+                            needComma = true;
+                            formatTimeMs(sb, totalPartial);
+                            sb.append("partial");
+                        }
+                        if (totalWindow != 0) {
+                            if (needComma) {
+                                sb.append(", ");
+                            }
+                            needComma = true;
+                            formatTimeMs(sb, totalWindow);
+                            sb.append("window");
+                        }
+                        sb.append(" realtime");
+                        pw.println(sb.toString());
                     }
                 }
             }
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 64d3d31..2b254af 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -194,6 +194,9 @@
      */
     public static final int FX_SURFACE_DIM     = 0x00020000;
 
+    /** @hide */
+    public static final int FX_SURFACE_SCREENSHOT   = 0x00030000;
+
     /** Mask used for FX values above @hide */
     public static final int FX_SURFACE_MASK     = 0x000F0000;
 
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index e16a8bd..5d01a0f 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -925,7 +925,7 @@
         event.setCurrentItemIndex(getSelectedItemPosition());
         event.setFromIndex(getFirstVisiblePosition());
         event.setToIndex(getLastVisiblePosition());
-        event.setItemCount(getAdapter().getCount());
+        event.setItemCount(getCount());
     }
 
     private boolean isScrollableForAccessibility() {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index e2a2566..3e96c81 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -98,6 +98,10 @@
     // in to one common name.
     private static final int MAX_WAKELOCKS_PER_UID = 30;
 
+    // The system process gets more.  It is special.  Oh so special.
+    // With, you know, special needs.  Like this.
+    private static final int MAX_WAKELOCKS_PER_UID_IN_SYSTEM = 50;
+
     private static final String BATCHED_WAKELOCK_NAME = "*overflow*";
 
     private static int sNumSpeedSteps;
@@ -2895,12 +2899,10 @@
                 String wakelockName = in.readString();
                 Uid.Wakelock wakelock = new Wakelock();
                 wakelock.readFromParcelLocked(unpluggables, in);
-                if (mWakelockStats.size() < MAX_WAKELOCKS_PER_UID) {
-                    // We will just drop some random set of wakelocks if
-                    // the previous run of the system was an older version
-                    // that didn't impose a limit.
-                    mWakelockStats.put(wakelockName, wakelock);
-                }
+                // We will just drop some random set of wakelocks if
+                // the previous run of the system was an older version
+                // that didn't impose a limit.
+                mWakelockStats.put(wakelockName, wakelock);
             }
 
             int numSensors = in.readInt();
@@ -3904,7 +3906,9 @@
         public StopwatchTimer getWakeTimerLocked(String name, int type) {
             Wakelock wl = mWakelockStats.get(name);
             if (wl == null) {
-                if (mWakelockStats.size() > MAX_WAKELOCKS_PER_UID) {
+                final int N = mWakelockStats.size();
+                if (N > MAX_WAKELOCKS_PER_UID && (mUid != Process.SYSTEM_UID
+                        || N > MAX_WAKELOCKS_PER_UID_IN_SYSTEM)) {
                     name = BATCHED_WAKELOCK_NAME;
                     wl = mWakelockStats.get(name);
                 }
diff --git a/docs/html/resources/dashboard/opengl.jd b/docs/html/resources/dashboard/opengl.jd
index 2b94b28..9089937 100644
--- a/docs/html/resources/dashboard/opengl.jd
+++ b/docs/html/resources/dashboard/opengl.jd
@@ -57,7 +57,7 @@
 <div class="dashboard-panel">
 
 <img alt="" width="400" height="250"
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1|GL%202.0%20%26%201.1&chd=t%3A9.4,90.6" />
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1|GL%202.0%20%26%201.1&chd=t%3A9.2,90.8" />
 
 <table>
 <tr>
@@ -66,14 +66,14 @@
 </tr>
 <tr>
 <td>1.1</th>
-<td>9.4%</td>
+<td>9.2%</td>
 </tr>
 <tr>
 <td>2.0</th>
-<td>90.6%</td>
+<td>90.8%</td>
 </tr>
 </table>
 
-<p><em>Data collected during a 7-day period ending on September 2, 2011</em></p>
+<p><em>Data collected during a 7-day period ending on October 3, 2011</em></p>
 </div>
 
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index 51cbae3..135c6f2 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -52,7 +52,7 @@
 <div class="dashboard-panel">
 
 <img alt="" height="250" width="470"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.0,1.8,13.3,51.2,0.6,30.7,0.2,0.7,0.5&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0|Android%203.1|Android%203.2&chco=c4df9b,6fad0c" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.1,1.4,11.7,45.3,0.5,38.2,0.2,0.9,0.7&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0|Android%203.1|Android%203.2&chco=c4df9b,6fad0c" />
 
 <table>
 <tr>
@@ -61,21 +61,21 @@
   <th>API Level</th>
   <th>Distribution</th>
 </tr>
-<tr><td><a href="{@docRoot}sdk/android-1.5.html">Android 1.5</a></td><td>Cupcake</td>  <td>3</td><td>1.0%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-1.6.html">Android 1.6</a></td><td>Donut</td>    <td>4</td><td>1.8%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-2.1.html">Android 2.1</a></td><td>Eclair</td>   <td>7</td><td>13.3%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-2.2.html">Android 2.2</a></td><td>Froyo</td>    <td>8</td><td>51.2%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-1.5.html">Android 1.5</a></td><td>Cupcake</td>  <td>3</td><td>1.1%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-1.6.html">Android 1.6</a></td><td>Donut</td>    <td>4</td><td>1.4%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-2.1.html">Android 2.1</a></td><td>Eclair</td>   <td>7</td><td>11.7%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-2.2.html">Android 2.2</a></td><td>Froyo</td>    <td>8</td><td>45.3%</td></tr>
 <tr><td><a href="{@docRoot}sdk/android-2.3.html">Android 2.3 -<br/>
-                             Android 2.3.2</a></td><td rowspan="2">Gingerbread</td>    <td>9</td><td>0.6%</td></tr>
+                             Android 2.3.2</a></td><td rowspan="2">Gingerbread</td>    <td>9</td><td>0.5%</td></tr>
 <tr><td><a href="{@docRoot}sdk/android-2.3.3.html">Android 2.3.3 -<br/>
-      Android 2.3.4</a></td><!-- Gingerbread -->                                       <td>10</td><td>30.7%</td></tr>
+      Android 2.3.7</a></td><!-- Gingerbread -->                                       <td>10</td><td>38.2%</td></tr>
 <tr><td><a href="{@docRoot}sdk/android-3.0.html">Android 3.0</a></td>
                                                    <td rowspan="3">Honeycomb</td>      <td>11</td><td>0.2%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-3.1.html">Android 3.1</a></td><!-- Honeycomb --><td>12</td><td>0.7%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-3.2.html">Android 3.2</a></td><!-- Honeycomb --><td>13</td><td>0.5%</td></tr> 
+<tr><td><a href="{@docRoot}sdk/android-3.1.html">Android 3.1</a></td><!-- Honeycomb --><td>12</td><td>0.9%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-3.2.html">Android 3.2</a></td><!-- Honeycomb --><td>13</td><td>0.7%</td></tr> 
 </table>
 
-<p><em>Data collected during a 14-day period ending on September 2, 2011</em></p>
+<p><em>Data collected during a 14-day period ending on October 3, 2011</em></p>
 <!--
 <p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
 -->
@@ -104,9 +104,9 @@
 <div class="dashboard-panel">
 
 <img alt="" height="250" width="660" style="padding:5px;background:#fff"
-src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C1%3A%7C2011%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2011%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:100.0,99.8,99.7,99.6,99.6,99.5,99.4,99.3,99.2,99.0,98.8,98.7,98.6|96.7,96.8,97.0,97.1,97.3,97.5,97.5,97.5,97.7,97.6,97.5,97.5,97.5|91.5,92.0,93.5,93.9,94.3,94.8,95.0,95.2,95.5,95.5,95.5,95.6,95.8|61.5,63.0,66.4,68.0,69.8,71.5,73.9,75.4,77.6,79.0,80.2,81.1,82.4|1.1,1.7,2.5,3.1,4.0,6.1,9.5,13.6,17.8,20.6,24.3,27.5,31.1|0.0,1.0,1.7,2.2,3.0,5.1,8.4,12.6,16.8,20.0,23.7,26.9,30.5&chm=b,c3df9b,0,1,0|b,b4db77,1,2,0|tAndroid 2.1,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|tAndroid 2.2,3f5e0e,3,0,15,,t::-5|b,96dd28,3,4,0|b,83c916,4,5,0|tAndroid 2.3.3,131d02,5,7,15,,t::-5|B,6fad0c,5,6,0&chg=7,25&chdl=Android 1.5|Android 1.6|Android 2.1|Android 2.2|Android 2.3|Android 2.3.3&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08" />
+src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C09/15%7C10/01%7C1%3A%7C2011%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2011%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.7,99.6,99.6,99.5,99.4,99.3,99.2,99.0,98.8,98.7,98.5,98.5,98.2|97.0,97.1,97.3,97.5,97.5,97.5,97.7,97.6,97.5,97.5,97.5,97.5,97.1|93.5,93.9,94.3,94.8,95.0,95.2,95.5,95.5,95.5,95.6,95.7,95.8,95.6|66.4,68.0,69.8,71.5,73.9,75.4,77.6,79.0,80.2,81.1,82.4,83.3,83.8|2.5,3.1,4.0,6.1,9.5,13.6,17.8,20.6,24.3,27.5,31.2,34.7,38.3|1.7,2.2,3.0,5.1,8.4,12.6,16.8,20.0,23.7,26.9,30.6,34.1,37.8&chm=b,c3df9b,0,1,0|b,b4db77,1,2,0|tAndroid 2.1,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|tAndroid 2.2,3f5e0e,3,0,15,,t::-5|b,96dd28,3,4,0|b,83c916,4,5,0|tAndroid 2.3.3,131d02,5,5,15,,t::-5|B,6fad0c,5,6,0&chg=7,25&chdl=Android 1.5|Android 1.6|Android 2.1|Android 2.2|Android 2.3|Android 2.3.3&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08" />
 
-<p><em>Last historical dataset collected during a 14-day period ending on September 2, 2011</em></p>
+<p><em>Last historical dataset collected during a 14-day period ending on October 3, 2011</em></p>
 
 
 </div><!-- end dashboard-panel -->
diff --git a/docs/html/resources/dashboard/screens.jd b/docs/html/resources/dashboard/screens.jd
index 77fd2d2..67b47d0 100644
--- a/docs/html/resources/dashboard/screens.jd
+++ b/docs/html/resources/dashboard/screens.jd
@@ -59,7 +59,8 @@
 
 <div class="dashboard-panel">
 
-<img alt="" width="400" height="250" src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=Xlarge%20/%20mdpi| Large%20/%20mdpi|Normal%20/%20hdpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Small%20/%20hdpi&chd=t%3A1.5, 3.2,74,0.9,16.9,3.5" />
+<img alt="" width="400" height="250"
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=Xlarge%20/%20mdpi|Large%20/%20ldpi|Large%20/%20mdpi|Normal%20/%20hdpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Small%20/%20hdpi|Small%20/%20ldpi&chd=t%3A2.6,0.1,3.0,71.9,0.9,17.6,2.7,1.2" />
 
 <table>
 <tr>
@@ -70,31 +71,31 @@
 <th scope="col">xhdpi</th>
 </tr>
 <tr><th scope="row">small</th> 
-<td></td>     <!-- small/ldpi -->
+<td>1.2%</td>     <!-- small/ldpi -->
 <td></td>     <!-- small/mdpi -->
-<td>3.5%</td> <!-- small/hdpi -->
+<td>2.7%</td> <!-- small/hdpi -->
 <td></td>     <!-- small/xhdpi -->
 </tr> 
 <tr><th scope="row">normal</th> 
 <td>0.9%</td>  <!-- normal/ldpi -->
-<td>16.9%</td> <!-- normal/mdpi -->
-<td>74%</td> <!-- normal/hdpi -->
+<td>17.6%</td> <!-- normal/mdpi -->
+<td>71.9%</td> <!-- normal/hdpi -->
 <td></td>      <!-- normal/xhdpi -->
 </tr> 
 <tr><th scope="row">large</th> 
-<td></td>     <!-- large/ldpi -->
-<td>3.2%</td> <!-- large/mdpi -->
+<td>0.1%</td>     <!-- large/ldpi -->
+<td>3.0%</td> <!-- large/mdpi -->
 <td></td>     <!-- large/hdpi -->
 <td></td>     <!-- large/xhdpi -->
 </tr> 
 <tr><th scope="row">xlarge</th> 
 <td></td>     <!-- xlarge/ldpi -->
-<td>1.5%</td> <!-- xlarge/mdpi -->
+<td>2.6%</td> <!-- xlarge/mdpi -->
 <td></td>     <!-- xlarge/hdpi -->
 <td></td>     <!-- xlarge/xhdpi -->
 </tr> 
 </table>
 
-<p><em>Data collected during a 7-day period ending on September 2, 2011</em></p>
+<p><em>Data collected during a 7-day period ending on October 3, 2011</em></p>
 </div>
 
diff --git a/docs/html/sdk/android-4.0.jd b/docs/html/sdk/android-4.0.jd
index da40d52..90319878 100644
--- a/docs/html/sdk/android-4.0.jd
+++ b/docs/html/sdk/android-4.0.jd
@@ -158,7 +158,7 @@
 <p>Adding a new raw contact for the profile requires the {@link
 android.Manifest.permission#WRITE_PROFILE} permission. Likewise, in order to read from the profile
 table, you must request the {@link android.Manifest.permission#READ_PROFILE} permission. However,
-most apps should need to read the user profile, even when contributing data to the
+most apps should not need to read the user profile, even when contributing data to the
 profile. Reading the user profile is a sensitive permission and you should expect users to be
 skeptical of apps that request it.</p>
 
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index ea022a6..e7a33f1 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -53,6 +53,7 @@
         eFXSurfaceNormal    = 0x00000000,
         eFXSurfaceBlur      = 0x00010000,
         eFXSurfaceDim       = 0x00020000,
+        eFXSurfaceScreenshot= 0x00030000,
         eFXSurfaceMask      = 0x000F0000,
     };
 
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index 0bee0f1..98fa171 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -409,9 +409,9 @@
 int SurfaceTextureClient::disconnect(int api) {
     LOGV("SurfaceTextureClient::disconnect");
     Mutex::Autolock lock(mMutex);
+    freeAllBuffers();
     int err = mSurfaceTexture->disconnect(api);
     if (!err) {
-        freeAllBuffers();
         mReqFormat = 0;
         mReqWidth = 0;
         mReqHeight = 0;
diff --git a/packages/SystemUI/res/anim/recent_appear.xml b/packages/SystemUI/res/anim/recent_appear.xml
index 20fe052..4400d9d 100644
--- a/packages/SystemUI/res/anim/recent_appear.xml
+++ b/packages/SystemUI/res/anim/recent_appear.xml
@@ -16,5 +16,5 @@
 
 <alpha xmlns:android="http://schemas.android.com/apk/res/android"
     android:fromAlpha="0.0" android:toAlpha="1.0"
-    android:duration="@android:integer/config_mediumAnimTime"
+    android:duration="@android:integer/config_shortAnimTime"
     />
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
index 83c4faf..2d76455 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -39,11 +39,11 @@
             android:layout_marginTop="@dimen/status_bar_recents_thumbnail_top_margin"
             android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
             android:background="@drawable/recents_thumbnail_bg"
-            android:foreground="@drawable/recents_thumbnail_fg">
+            android:foreground="@drawable/recents_thumbnail_fg"
+            android:visibility="invisible">
             <ImageView android:id="@+id/app_thumbnail_image"
                 android:layout_width="@dimen/status_bar_recents_thumbnail_width"
                 android:layout_height="@dimen/status_bar_recents_thumbnail_height"
-                android:visibility="invisible"
             />
         </FrameLayout>
 
@@ -58,7 +58,6 @@
             android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
             android:scaleType="centerInside"
             android:adjustViewBounds="true"
-            android:visibility="invisible"
         />
 
         <TextView android:id="@+id/app_label"
@@ -74,7 +73,6 @@
             android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
             android:singleLine="true"
             android:ellipsize="marquee"
-            android:visibility="invisible"
             android:textColor="@color/status_bar_recents_app_label_color"
         />
 
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
index 3d8b9d6..b653fcd 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
@@ -52,11 +52,11 @@
             android:layout_toRightOf="@id/app_label"
             android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
             android:background="@drawable/recents_thumbnail_bg"
-            android:foreground="@drawable/recents_thumbnail_fg">
+            android:foreground="@drawable/recents_thumbnail_fg"
+            android:visibility="invisible">
             <ImageView android:id="@+id/app_thumbnail_image"
                 android:layout_width="@dimen/status_bar_recents_thumbnail_width"
                 android:layout_height="@dimen/status_bar_recents_thumbnail_height"
-                android:visibility="invisible"
             />
         </FrameLayout>
         <View android:id="@+id/recents_callout_line"
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
index e6336718..18a31f7 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
@@ -31,11 +31,11 @@
         android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
         android:scaleType="center"
         android:background="@drawable/recents_thumbnail_bg"
-        android:foreground="@drawable/recents_thumbnail_fg">
+        android:foreground="@drawable/recents_thumbnail_fg"
+        android:visibility="invisible">
         <ImageView android:id="@+id/app_thumbnail_image"
             android:layout_width="@dimen/status_bar_recents_thumbnail_width"
             android:layout_height="@dimen/status_bar_recents_thumbnail_height"
-            android:visibility="invisible"
         />
         <ImageView android:id="@+id/app_icon"
             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 47aa849..dcda9c2 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -86,8 +86,8 @@
         mIconDpi = isTablet ? DisplayMetrics.DENSITY_HIGH : res.getDisplayMetrics().densityDpi;
 
         // Render the default thumbnail background
-        int width = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_width);
-        int height = (int) res.getDimension(R.dimen.status_bar_recents_thumbnail_height);
+        int width = (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+        int height = (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
         int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
 
         mDefaultThumbnailBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
@@ -106,6 +106,10 @@
         mRecentsPanel = recentsPanel;
     }
 
+    public Bitmap getDefaultThumbnail() {
+        return mDefaultThumbnailBackground;
+    }
+
     // Create an TaskDescription, returning null if the title or icon is null, or if it's the
     // home activity
     TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent,
@@ -278,7 +282,7 @@
                             TaskDescription td = descriptions.get(i);
                             loadThumbnail(td);
                             long now = SystemClock.uptimeMillis();
-                            nextTime += 150;
+                            nextTime += 0;
                             if (nextTime > now) {
                                 try {
                                     Thread.sleep(nextTime-now);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index bd1fcfc..343b33514 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -37,6 +37,7 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
@@ -57,8 +58,8 @@
 import com.android.systemui.statusbar.tablet.StatusBarPanel;
 import com.android.systemui.statusbar.tablet.TabletStatusBar;
 
-public class RecentsPanelView extends RelativeLayout
-        implements OnItemClickListener, RecentsCallback, StatusBarPanel, Animator.AnimatorListener {
+public class RecentsPanelView extends RelativeLayout implements OnItemClickListener, RecentsCallback,
+        StatusBarPanel, Animator.AnimatorListener, View.OnTouchListener {
     static final String TAG = "RecentsPanelView";
     static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
     private Context mContext;
@@ -74,6 +75,7 @@
 
     private RecentTasksLoader mRecentTasksLoader;
     private ArrayList<TaskDescription> mRecentTaskDescriptions;
+    private boolean mRecentTasksDirty = true;
     private TaskDescriptionAdapter mListAdapter;
     private int mThumbnailWidth;
 
@@ -94,6 +96,7 @@
     /* package */ final static class ViewHolder {
         View thumbnailView;
         ImageView thumbnailViewImage;
+        Bitmap thumbnailViewImageBitmap;
         ImageView iconView;
         TextView labelView;
         TextView descriptionView;
@@ -127,6 +130,10 @@
                 holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail);
                 holder.thumbnailViewImage = (ImageView) convertView.findViewById(
                         R.id.app_thumbnail_image);
+                // If we set the default thumbnail now, we avoid an onLayout when we update
+                // the thumbnail later (if they both have the same dimensions)
+                updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
+
                 holder.iconView = (ImageView) convertView.findViewById(R.id.app_icon);
                 holder.labelView = (TextView) convertView.findViewById(R.id.app_label);
                 holder.descriptionView = (TextView) convertView.findViewById(R.id.app_description);
@@ -139,12 +146,15 @@
             // index is reverse since most recent appears at the bottom...
             final int index = mRecentTaskDescriptions.size() - position - 1;
 
-            final TaskDescription taskDescription = mRecentTaskDescriptions.get(index);
-            applyTaskDescription(holder, taskDescription, false);
+            final TaskDescription td = mRecentTaskDescriptions.get(index);
+            holder.iconView.setImageDrawable(td.getIcon());
+            holder.labelView.setText(td.getLabel());
+            holder.thumbnailView.setContentDescription(td.getLabel());
+            updateThumbnail(holder, td.getThumbnail(), true, false);
 
-            holder.thumbnailView.setTag(taskDescription);
+            holder.thumbnailView.setTag(td);
             holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView));
-            holder.taskDescription = taskDescription;
+            holder.taskDescription = td;
 
             return convertView;
         }
@@ -193,6 +203,7 @@
             }
         } else {
             mRecentTasksLoader.cancelLoadingThumbnails();
+            mRecentTasksDirty = true;
         }
         if (animate) {
             if (mShowing != show) {
@@ -250,9 +261,7 @@
             createCustomAnimations(transitioner);
         } else {
             ((ViewGroup)mRecentsContainer).setLayoutTransition(null);
-            // Clear memory used by screenshots
-            mRecentTaskDescriptions.clear();
-            mListAdapter.notifyDataSetInvalidated();
+            clearRecentTasksList();
         }
     }
 
@@ -374,47 +383,33 @@
         }
     }
 
-
-    void applyTaskDescription(ViewHolder h, TaskDescription td, boolean anim) {
-        h.iconView.setImageDrawable(td.getIcon());
-        if (h.iconView.getVisibility() != View.VISIBLE) {
-            if (anim) {
-                h.iconView.setAnimation(AnimationUtils.loadAnimation(
-                        mContext, R.anim.recent_appear));
-            }
-            h.iconView.setVisibility(View.VISIBLE);
-        }
-        h.labelView.setText(td.getLabel());
-        h.thumbnailView.setContentDescription(td.getLabel());
-        if (h.labelView.getVisibility() != View.VISIBLE) {
-            if (anim) {
-                h.labelView.setAnimation(AnimationUtils.loadAnimation(
-                        mContext, R.anim.recent_appear));
-            }
-            h.labelView.setVisibility(View.VISIBLE);
-        }
-        Bitmap thumbnail = td.getThumbnail();
+    private void updateThumbnail(ViewHolder h, Bitmap thumbnail, boolean show, boolean anim) {
         if (thumbnail != null) {
             // Should remove the default image in the frame
             // that this now covers, to improve scrolling speed.
             // That can't be done until the anim is complete though.
             h.thumbnailViewImage.setImageBitmap(thumbnail);
-            // scale to fill up the full width
-            Matrix scaleMatrix = new Matrix();
-            float scale = mThumbnailWidth / (float) thumbnail.getWidth();
-            scaleMatrix.setScale(scale, scale);
-            h.thumbnailViewImage.setScaleType(ScaleType.MATRIX);
-            h.thumbnailViewImage.setImageMatrix(scaleMatrix);
-            if (h.thumbnailViewImage.getVisibility() != View.VISIBLE) {
-                if (anim) {
-                    h.thumbnailViewImage.setAnimation(
-                            AnimationUtils.loadAnimation(
-                                    mContext, R.anim.recent_appear));
-                }
-                h.thumbnailViewImage.setVisibility(View.VISIBLE);
+
+            // scale the image to fill the full width of the ImageView. do this only if
+            // we haven't set a bitmap before, or if the bitmap size has changed
+            if (h.thumbnailViewImageBitmap == null ||
+                h.thumbnailViewImageBitmap.getWidth() != thumbnail.getWidth() ||
+                h.thumbnailViewImageBitmap.getHeight() != thumbnail.getHeight()) {
+                Matrix scaleMatrix = new Matrix();
+                float scale = mThumbnailWidth / (float) thumbnail.getWidth();
+                scaleMatrix.setScale(scale, scale);
+                h.thumbnailViewImage.setScaleType(ScaleType.MATRIX);
+                h.thumbnailViewImage.setImageMatrix(scaleMatrix);
             }
+            if (show && h.thumbnailView.getVisibility() != View.VISIBLE) {
+                if (anim) {
+                    h.thumbnailView.setAnimation(
+                            AnimationUtils.loadAnimation(mContext, R.anim.recent_appear));
+                }
+                h.thumbnailView.setVisibility(View.VISIBLE);
+            }
+            h.thumbnailViewImageBitmap = thumbnail;
         }
-        //h.descriptionView.setText(ad.description);
     }
 
     void onTaskThumbnailLoaded(TaskDescription ad) {
@@ -432,7 +427,11 @@
                     if (v.getTag() instanceof ViewHolder) {
                         ViewHolder h = (ViewHolder)v.getTag();
                         if (h.taskDescription == ad) {
-                            applyTaskDescription(h, ad, true);
+                            // only fade in the thumbnail if recents is already visible-- we
+                            // show it immediately otherwise
+                            boolean animateShow = mShowing &&
+                                mRecentsGlowView.getAlpha() > ViewConfiguration.ALPHA_THRESHOLD;
+                            updateThumbnail(h, ad.getThumbnail(), true, animateShow);
                         }
                     }
                 }
@@ -440,14 +439,55 @@
         }
     }
 
-    private void refreshRecentTasksList(ArrayList<TaskDescription> recentTasksList) {
-        if (recentTasksList != null) {
-            mRecentTaskDescriptions = recentTasksList;
-        } else {
-            mRecentTaskDescriptions = mRecentTasksLoader.getRecentTasks();
+    // additional optimization when we have sofware system buttons - start loading the recent
+    // tasks on touch down
+    @Override
+    public boolean onTouch(View v, MotionEvent ev) {
+        if (!mShowing) {
+            int action = ev.getAction() & MotionEvent.ACTION_MASK;
+            if (action == MotionEvent.ACTION_DOWN) {
+                // If we set our visibility to INVISIBLE here, we avoid an extra call to onLayout
+                // later when we become visible
+                setVisibility(INVISIBLE);
+                refreshRecentTasksList();
+            } else if (action == MotionEvent.ACTION_CANCEL) {
+                setVisibility(GONE);
+                clearRecentTasksList();
+            } else if (action == MotionEvent.ACTION_UP) {
+                if (!v.isPressed()) {
+                    setVisibility(GONE);
+                    clearRecentTasksList();
+                }
+            }
         }
-        mListAdapter.notifyDataSetInvalidated();
-        updateUiElements(getResources().getConfiguration());
+        return false;
+    }
+
+    public void clearRecentTasksList() {
+        // Clear memory used by screenshots
+        if (mRecentTaskDescriptions != null) {
+            mRecentTasksLoader.cancelLoadingThumbnails();
+            mRecentTaskDescriptions.clear();
+            mListAdapter.notifyDataSetInvalidated();
+            mRecentTasksDirty = true;
+        }
+    }
+
+    public void refreshRecentTasksList() {
+        refreshRecentTasksList(null);
+    }
+
+    private void refreshRecentTasksList(ArrayList<TaskDescription> recentTasksList) {
+        if (mRecentTasksDirty) {
+            if (recentTasksList != null) {
+                mRecentTaskDescriptions = recentTasksList;
+            } else {
+                mRecentTaskDescriptions = mRecentTasksLoader.getRecentTasks();
+            }
+            mListAdapter.notifyDataSetInvalidated();
+            updateUiElements(getResources().getConfiguration());
+            mRecentTasksDirty = false;
+        }
     }
 
     public ArrayList<TaskDescription> getRecentTasksList() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index b724552..352d4a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -113,6 +113,8 @@
     // will likely move to a resource or other tunable param at some point
     private static final int INTRUDER_ALERT_DECAY_MS = 10000;
 
+    private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
+
     // fling gesture tuning parameters, scaled to display density
     private float mSelfExpandVelocityPx; // classic value: 2000px/s
     private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
@@ -435,13 +437,18 @@
         }
     };
 
+    private void prepareNavigationBarView() {
+        mNavigationBarView.reorient();
+
+        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
+        mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel);
+    }
+
     // For small-screen devices (read: phones) that lack hardware navigation buttons
     private void addNavigationBar() {
         if (mNavigationBarView == null) return;
 
-        mNavigationBarView.reorient();
-
-        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
+        prepareNavigationBarView();
 
         WindowManagerImpl.getDefault().addView(
                 mNavigationBarView, getNavigationBarLayoutParams());
@@ -450,9 +457,7 @@
     private void repositionNavigationBar() {
         if (mNavigationBarView == null) return;
 
-        mNavigationBarView.reorient();
-
-        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
+        prepareNavigationBarView();
 
         WindowManagerImpl.getDefault().updateViewLayout(
                 mNavigationBarView, getNavigationBarLayoutParams());
@@ -695,6 +700,10 @@
 
             // Recalculate the position of the sliding windows and the titles.
             updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+
+            if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0 && !mAnimating) {
+                animateCollapse();
+            }
         }
 
         setAreThereNotifications();
@@ -2007,8 +2016,8 @@
     }
 
     public void toggleRecentApps() {
-        int msg = (mRecentsPanel.getVisibility() == View.GONE)
-                ? MSG_OPEN_RECENTS_PANEL : MSG_CLOSE_RECENTS_PANEL;
+        int msg = (mRecentsPanel.getVisibility() == View.VISIBLE)
+                ? MSG_CLOSE_RECENTS_PANEL : MSG_OPEN_RECENTS_PANEL;
         mHandler.removeMessages(msg);
         mHandler.sendEmptyMessage(msg);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index f0a10f3..de8226b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -587,6 +587,7 @@
 
         // Add the windows
         addPanelWindows();
+        mRecentButton.setOnTouchListener(mRecentsPanel);
 
         mPile = (ViewGroup)mNotificationPanel.findViewById(R.id.content);
         mPile.removeAllViews();
@@ -1805,8 +1806,8 @@
     }
 
     public void toggleRecentApps() {
-        int msg = (mRecentsPanel.getVisibility() == View.GONE)
-                ? MSG_OPEN_RECENTS_PANEL : MSG_CLOSE_RECENTS_PANEL;
+        int msg = (mRecentsPanel.getVisibility() == View.VISIBLE)
+                ? MSG_CLOSE_RECENTS_PANEL : MSG_OPEN_RECENTS_PANEL;
         mHandler.removeMessages(msg);
         mHandler.sendEmptyMessage(msg);
     }
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 3e0304f..69560e5 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1332,7 +1332,13 @@
                                                             int sessionId)
 {
     Mutex::Autolock _l(mLock);
+    checkSuspendOnEffectEnabled_l(effect, enabled, sessionId);
+}
 
+void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect,
+                                                            bool enabled,
+                                                            int sessionId)
+{
     if (mType != RECORD) {
         // suspend all effects in AUDIO_SESSION_OUTPUT_MIX when enabling any effect on
         // another session. This gives the priority to well behaved effect control panels
@@ -5224,6 +5230,9 @@
                     sp<EffectHandle> handle = effect->mHandles[j].promote();
                     if (handle != 0) {
                         handle->mEffect.clear();
+                        if (handle->mHasControl && handle->mEnabled) {
+                            t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId());
+                        }
                     }
                 }
                 AudioSystem::unregisterEffect(effect->id());
@@ -6844,7 +6853,7 @@
     }
     mEffect->disconnect(this, unpiniflast);
 
-    if (mEnabled) {
+    if (mHasControl && mEnabled) {
         sp<ThreadBase> thread = mEffect->thread().promote();
         if (thread != 0) {
             thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index ed9d81e..4b794ef 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -492,10 +492,12 @@
                                             int sessionId = AUDIO_SESSION_OUTPUT_MIX);
                     // check if some effects must be suspended/restored when an effect is enabled
                     // or disabled
-        virtual     void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
+                    void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
                                                      bool enabled,
                                                      int sessionId = AUDIO_SESSION_OUTPUT_MIX);
-
+                    void checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect,
+                                                       bool enabled,
+                                                       int sessionId = AUDIO_SESSION_OUTPUT_MIX);
         mutable     Mutex                   mLock;
 
     protected:
@@ -1299,7 +1301,7 @@
         // suspend all eligible effects
         void setEffectSuspendedAll_l(bool suspend);
         // check if effects should be suspend or restored when a given effect is enable or disabled
-        virtual void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
+        void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
                                               bool enabled);
 
         status_t dump(int fd, const Vector<String16>& args);
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index f1b8bae..47644de 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -44,6 +44,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IPowerManager;
@@ -1656,8 +1657,18 @@
         }
     }
 
+    private boolean isExtStorageEncrypted() {
+        String state = SystemProperties.get("vold.decrypt");
+        return !"".equals(state);
+    }
+
     void wipeDataLocked(int flags) {
-        if ((flags&DevicePolicyManager.WIPE_EXTERNAL_STORAGE) != 0) {
+        // If the SD card is encrypted and non-removable, we have to force a wipe.
+        boolean forceExtWipe = !Environment.isExternalStorageRemovable() && isExtStorageEncrypted();
+        boolean wipeExtRequested = (flags&DevicePolicyManager.WIPE_EXTERNAL_STORAGE) != 0;
+
+        // Note: we can only do the wipe via ExternalStorageFormatter if the volume is not emulated.
+        if ((forceExtWipe || wipeExtRequested) && !Environment.isExternalStorageEmulated()) {
             Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET);
             intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
             mWakeLock.acquire(10000);
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index 788ecda..a653322 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -691,7 +691,7 @@
                 }
                 ISpellCheckerService spellChecker = ISpellCheckerService.Stub.asInterface(service);
                 final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId);
-                if (this == group.mInternalConnection) {
+                if (group != null && this == group.mInternalConnection) {
                     group.onServiceConnected(spellChecker);
                 }
             }
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 91576e7..131f11c 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -64,17 +64,16 @@
             boolean inTransaction, int originalWidth, int originalHeight, int originalRotation) {
         mContext = context;
 
-        Bitmap screenshot = Surface.screenshot(0, 0);
-
-        if (screenshot == null) {
-            // Device is not capable of screenshots...  we can't do an animation.
-            return;
-        }
-
         // Screenshot does NOT include rotation!
         mSnapshotRotation = 0;
-        mWidth = screenshot.getWidth();
-        mHeight = screenshot.getHeight();
+        if (originalRotation == Surface.ROTATION_90
+                || originalRotation == Surface.ROTATION_270) {
+            mWidth = originalHeight;
+            mHeight = originalWidth;
+        } else {
+            mWidth = originalWidth;
+            mHeight = originalHeight;
+        }
 
         mOriginalRotation = originalRotation;
         mOriginalWidth = originalWidth;
@@ -89,7 +88,12 @@
         try {
             try {
                 mSurface = new Surface(session, 0, "FreezeSurface",
-                        -1, mWidth, mHeight, PixelFormat.OPAQUE, 0);
+                        -1, mWidth, mHeight, PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT);
+                if (mSurface == null || !mSurface.isValid()) {
+                    // Screenshot failed, punt.
+                    mSurface = null;
+                    return;
+                }
                 mSurface.setLayer(FREEZE_LAYER + 1);
             } catch (Surface.OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate freeze surface", e);
@@ -100,38 +104,12 @@
                             "  FREEZE " + mSurface + ": CREATE");
 
             setRotation(originalRotation);
-
-            if (mSurface != null) {
-                Rect dirty = new Rect(0, 0, mWidth, mHeight);
-                Canvas c = null;
-                try {
-                    c = mSurface.lockCanvas(dirty);
-                } catch (IllegalArgumentException e) {
-                    Slog.w(TAG, "Unable to lock surface", e);
-                } catch (Surface.OutOfResourcesException e) {
-                    Slog.w(TAG, "Unable to lock surface", e);
-                }
-                if (c == null) {
-                    Slog.w(TAG, "Null surface canvas");
-                    mSurface.destroy();
-                    mSurface = null;
-                    return;
-                }
-
-                Paint paint = new Paint(0);
-                paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
-                c.drawBitmap(screenshot, 0, 0, paint);
-
-                mSurface.unlockCanvasAndPost(c);
-            }
         } finally {
             if (!inTransaction) {
                 Surface.closeTransaction();
                 if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG,
                         "<<< CLOSE TRANSACTION ScreenRotationAnimation");
             }
-    
-            screenshot.recycle();
         }
     }
 
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index e921818..eeffb02 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -1120,7 +1120,11 @@
                 // window's center).
                 final float w = frame.width();
                 final float h = frame.height();
-                tmpMatrix.setScale(1 + 2/w, 1 + 2/h, w/2, h/2);
+                if (w>=1 && h>=1) {
+                    tmpMatrix.setScale(1 + 2/w, 1 + 2/h, w/2, h/2);
+                } else {
+                    tmpMatrix.reset();
+                }
             } else {
                 tmpMatrix.reset();
             }
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index dab0705..61a8358 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -5,6 +5,7 @@
     Layer.cpp 								\
     LayerBase.cpp 							\
     LayerDim.cpp 							\
+    LayerScreenshot.cpp						\
     DdmConnection.cpp						\
     DisplayHardware/DisplayHardware.cpp 	\
     DisplayHardware/DisplayHardwareBase.cpp \
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 34a0d73..f885fc7 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -88,16 +88,8 @@
 
 Layer::~Layer()
 {
-    class MessageDestroyGLState : public MessageBase {
-        GLuint texture;
-    public:
-        MessageDestroyGLState(GLuint texture) : texture(texture) { }
-        virtual bool handler() {
-            glDeleteTextures(1, &texture);
-            return true;
-        }
-    };
-    mFlinger->postMessageAsync( new MessageDestroyGLState(mTextureName) );
+    mFlinger->postMessageAsync(
+            new SurfaceFlinger::MessageDestroyGLTexture(mTextureName) );
 }
 
 void Layer::onFrameQueued() {
diff --git a/services/surfaceflinger/LayerScreenshot.cpp b/services/surfaceflinger/LayerScreenshot.cpp
new file mode 100644
index 0000000..e30ccbf
--- /dev/null
+++ b/services/surfaceflinger/LayerScreenshot.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include "LayerScreenshot.h"
+#include "SurfaceFlinger.h"
+#include "DisplayHardware/DisplayHardware.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+LayerScreenshot::LayerScreenshot(SurfaceFlinger* flinger, DisplayID display,
+        const sp<Client>& client)
+    : LayerBaseClient(flinger, display, client),
+      mTextureName(0), mFlinger(flinger)
+{
+}
+
+LayerScreenshot::~LayerScreenshot()
+{
+    if (mTextureName) {
+        mFlinger->postMessageAsync(
+                new SurfaceFlinger::MessageDestroyGLTexture(mTextureName) );
+    }
+}
+
+status_t LayerScreenshot::capture() {
+    GLfloat u, v;
+    status_t result = mFlinger->renderScreenToTexture(0, &mTextureName, &u, &v);
+    if (result != NO_ERROR) {
+        return result;
+    }
+
+    glBindTexture(GL_TEXTURE_2D, mTextureName);
+    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+    mTexCoords[0] = 0;         mTexCoords[1] = v;
+    mTexCoords[2] = 0;         mTexCoords[3] = 0;
+    mTexCoords[4] = u;         mTexCoords[5] = 0;
+    mTexCoords[6] = u;         mTexCoords[7] = v;
+
+    return NO_ERROR;
+}
+
+void LayerScreenshot::onDraw(const Region& clip) const
+{
+    const State& s(drawingState());
+    Region::const_iterator it = clip.begin();
+    Region::const_iterator const end = clip.end();
+    if (s.alpha>0 && (it != end)) {
+        const DisplayHardware& hw(graphicPlane(0).displayHardware());
+        const GLfloat alpha = s.alpha/255.0f;
+        const uint32_t fbHeight = hw.getHeight();
+
+        if (s.alpha == 0xFF) {
+            glDisable(GL_BLEND);
+        } else {
+            glEnable(GL_BLEND);
+            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        }
+
+        glColor4f(0, 0, 0, alpha);
+
+        glDisable(GL_TEXTURE_EXTERNAL_OES);
+        glEnable(GL_TEXTURE_2D);
+
+        glBindTexture(GL_TEXTURE_2D, mTextureName);
+        glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+        glMatrixMode(GL_TEXTURE);
+        glLoadIdentity();
+        glMatrixMode(GL_MODELVIEW);
+
+        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+        glTexCoordPointer(2, GL_FLOAT, 0, mTexCoords);
+        glVertexPointer(2, GL_FLOAT, 0, mVertices);
+
+        while (it != end) {
+            const Rect& r = *it++;
+            const GLint sy = fbHeight - (r.top + r.height());
+            glScissor(r.left, sy, r.width(), r.height());
+            glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+        }
+
+        glDisable(GL_BLEND);
+        glDisable(GL_TEXTURE_2D);
+        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    }
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/services/surfaceflinger/LayerScreenshot.h b/services/surfaceflinger/LayerScreenshot.h
new file mode 100644
index 0000000..e3a2b19
--- /dev/null
+++ b/services/surfaceflinger/LayerScreenshot.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef ANDROID_LAYER_SCREENSHOT_H
+#define ANDROID_LAYER_SCREENSHOT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "LayerBase.h"
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+class LayerScreenshot : public LayerBaseClient
+{
+    GLuint mTextureName;
+    GLfloat mTexCoords[8];
+    sp<SurfaceFlinger> mFlinger;
+public:    
+            LayerScreenshot(SurfaceFlinger* flinger, DisplayID display,
+                        const sp<Client>& client);
+        virtual ~LayerScreenshot();
+
+        status_t capture();
+
+    virtual void onDraw(const Region& clip) const;
+    virtual bool isOpaque() const         { return false; }
+    virtual bool isSecure() const         { return false; }
+    virtual bool isProtectedByApp() const { return false; }
+    virtual bool isProtectedByDRM() const { return false; }
+    virtual const char* getTypeId() const { return "LayerScreenshot"; }
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_LAYER_SCREENSHOT_H
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 595ec1e..ad1995d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -50,6 +50,7 @@
 #include "DdmConnection.h"
 #include "Layer.h"
 #include "LayerDim.h"
+#include "LayerScreenshot.h"
 #include "SurfaceFlinger.h"
 
 #include "DisplayHardware/DisplayHardware.h"
@@ -1358,6 +1359,9 @@
         case eFXSurfaceDim:
             layer = createDimSurface(client, d, w, h, flags);
             break;
+        case eFXSurfaceScreenshot:
+            layer = createScreenshotSurface(client, d, w, h, flags);
+            break;
     }
 
     if (layer != 0) {
@@ -1420,7 +1424,19 @@
         uint32_t w, uint32_t h, uint32_t flags)
 {
     sp<LayerDim> layer = new LayerDim(this, display, client);
-    layer->initStates(w, h, flags);
+    return layer;
+}
+
+sp<LayerScreenshot> SurfaceFlinger::createScreenshotSurface(
+        const sp<Client>& client, DisplayID display,
+        uint32_t w, uint32_t h, uint32_t flags)
+{
+    sp<LayerScreenshot> layer = new LayerScreenshot(this, display, client);
+    status_t err = layer->capture();
+    if (err != NO_ERROR) {
+        layer.clear();
+        LOGW("createScreenshotSurface failed (%s)", strerror(-err));
+    }
     return layer;
 }
 
@@ -1783,6 +1799,13 @@
 
 // ---------------------------------------------------------------------------
 
+status_t SurfaceFlinger::renderScreenToTexture(DisplayID dpy,
+        GLuint* textureName, GLfloat* uOut, GLfloat* vOut)
+{
+    Mutex::Autolock _l(mStateLock);
+    return renderScreenToTextureLocked(dpy, textureName, uOut, vOut);
+}
+
 status_t SurfaceFlinger::renderScreenToTextureLocked(DisplayID dpy,
         GLuint* textureName, GLfloat* uOut, GLfloat* vOut)
 {
@@ -1833,6 +1856,8 @@
         layer->drawForSreenShot();
     }
 
+    hw.compositionComplete();
+
     // back to main framebuffer
     glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
     glDisable(GL_SCISSOR_TEST);
@@ -1848,11 +1873,6 @@
 
 status_t SurfaceFlinger::electronBeamOffAnimationImplLocked()
 {
-    status_t result = PERMISSION_DENIED;
-
-    if (!GLExtensions::getInstance().haveFramebufferObject())
-        return INVALID_OPERATION;
-
     // get screen geometry
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
     const uint32_t hw_w = hw.getWidth();
@@ -1861,7 +1881,7 @@
 
     GLfloat u, v;
     GLuint tname;
-    result = renderScreenToTextureLocked(0, &tname, &u, &v);
+    status_t result = renderScreenToTextureLocked(0, &tname, &u, &v);
     if (result != NO_ERROR) {
         return result;
     }
@@ -2038,10 +2058,6 @@
         return result;
     }
 
-    // back to main framebuffer
-    glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
-    glDisable(GL_SCISSOR_TEST);
-
     GLfloat vtx[8];
     const GLfloat texCoords[4][2] = { {0,v}, {0,0}, {u,0}, {u,v} };
     glBindTexture(GL_TEXTURE_2D, tname);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0e642c1..3c8f4e5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -49,6 +49,7 @@
 class FreezeLock;
 class Layer;
 class LayerDim;
+class LayerScreenshot;
 struct surface_flinger_cblk_t;
 
 #define LIKELY( exp )       (__builtin_expect( (exp) != 0, true  ))
@@ -186,6 +187,15 @@
             void                        screenReleased(DisplayID dpy);
             void                        screenAcquired(DisplayID dpy);
 
+            status_t renderScreenToTexture(DisplayID dpy,
+                    GLuint* textureName, GLfloat* uOut, GLfloat* vOut);
+
+            status_t postMessageAsync(const sp<MessageBase>& msg,
+                    nsecs_t reltime=0, uint32_t flags = 0);
+
+            status_t postMessageSync(const sp<MessageBase>& msg,
+                    nsecs_t reltime=0, uint32_t flags = 0);
+
     status_t removeLayer(const sp<LayerBase>& layer);
     status_t addLayer(const sp<LayerBase>& layer);
     status_t invalidateLayerVisibility(const sp<LayerBase>& layer);
@@ -195,6 +205,18 @@
 
     GLuint getProtectedTexName() const { return mProtectedTexName; }
 
+
+    class MessageDestroyGLTexture : public MessageBase {
+        GLuint texture;
+    public:
+        MessageDestroyGLTexture(GLuint texture) : texture(texture) { }
+        virtual bool handler() {
+            glDeleteTextures(1, &texture);
+            return true;
+        }
+    };
+
+
 private:
     // DeathRecipient interface
     virtual void binderDied(const wp<IBinder>& who);
@@ -204,7 +226,6 @@
     friend class LayerBase;
     friend class LayerBaseClient;
     friend class Layer;
-    friend class LayerDim;
 
     sp<ISurface> createSurface(
             ISurfaceComposerClient::surface_data_t* params,
@@ -222,6 +243,10 @@
             const sp<Client>& client, DisplayID display,
             uint32_t w, uint32_t h, uint32_t flags);
 
+    sp<LayerScreenshot> createScreenshotSurface(
+            const sp<Client>& client, DisplayID display,
+            uint32_t w, uint32_t h, uint32_t flags);
+
     status_t removeSurface(const sp<Client>& client, SurfaceID sid);
     status_t destroySurface(const wp<LayerBaseClient>& layer);
     uint32_t setClientStateLocked(const sp<Client>& client, const layer_state_t& s);
@@ -329,12 +354,6 @@
 
     mutable     MessageQueue    mEventQueue;
 
-    status_t postMessageAsync(const sp<MessageBase>& msg,
-            nsecs_t reltime=0, uint32_t flags = 0);
-
-    status_t postMessageSync(const sp<MessageBase>& msg,
-            nsecs_t reltime=0, uint32_t flags = 0);
-
                 // access must be protected by mStateLock
     mutable     Mutex                   mStateLock;
                 State                   mCurrentState;