Merge "Switch to system process before modifying rotation."
diff --git a/api/current.txt b/api/current.txt
index bbf9302..d661daa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5848,6 +5848,8 @@
     field public static final java.lang.String ACTION_GTALK_SERVICE_CONNECTED = "android.intent.action.GTALK_CONNECTED";
     field public static final java.lang.String ACTION_GTALK_SERVICE_DISCONNECTED = "android.intent.action.GTALK_DISCONNECTED";
     field public static final java.lang.String ACTION_HEADSET_PLUG = "android.intent.action.HEADSET_PLUG";
+    field public static final java.lang.String ACTION_IDLE_MAINTENANCE_END = "android.intent.action.ACTION_IDLE_MAINTENANCE_END";
+    field public static final java.lang.String ACTION_IDLE_MAINTENANCE_START = "android.intent.action.ACTION_IDLE_MAINTENANCE_START";
     field public static final java.lang.String ACTION_INPUT_METHOD_CHANGED = "android.intent.action.INPUT_METHOD_CHANGED";
     field public static final java.lang.String ACTION_INSERT = "android.intent.action.INSERT";
     field public static final java.lang.String ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT";
@@ -11449,6 +11451,11 @@
     field public static final int MPEG4ProfileSimpleFBA = 128; // 0x80
     field public static final int MPEG4ProfileSimpleFace = 64; // 0x40
     field public static final int MPEG4ProfileSimpleScalable = 2; // 0x2
+    field public static final int VP8Level_Version0 = 1; // 0x1
+    field public static final int VP8Level_Version1 = 2; // 0x2
+    field public static final int VP8Level_Version2 = 4; // 0x4
+    field public static final int VP8Level_Version3 = 8; // 0x8
+    field public static final int VP8ProfileMain = 1; // 0x1
     field public int level;
     field public int profile;
   }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 434946c..bbf3b69 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2319,6 +2319,61 @@
             "android.intent.action.DOCK_EVENT";
 
     /**
+     * Broadcast Action: A broadcast when idle maintenance can be started.
+     * This means that the user is not interacting with the device and is
+     * not expected to do so soon. Typical use of the idle maintenance is
+     * to perform somehow expensive tasks that can be postponed at a moment
+     * when they will not degrade user experience.
+     * <p>
+     * <p class="note">In order to keep the device responsive in case of an
+     * unexpected user interaction, implementations of a maintenance task
+     * should be interruptible. In such a scenario a broadcast with action
+     * {@link #ACTION_IDLE_MAINTENANCE_END} will be sent. In other words, you
+     * should not do the maintenance work in
+     * {@link BroadcastReceiver#onReceive(Context, Intent)}, rather start a
+     * maintenance service by {@link Context#startService(Intent)}. Also
+     * you should hold a wake lock while your maintenance service is running
+     * to prevent the device going to sleep.
+     * </p>
+     * <p>
+     * <p class="note">This is a protected intent that can only be sent by
+     * the system.
+     * </p>
+     *
+     * @see #ACTION_IDLE_MAINTENANCE_END
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_IDLE_MAINTENANCE_START =
+            "android.intent.action.ACTION_IDLE_MAINTENANCE_START";
+
+    /**
+     * Broadcast Action:  A broadcast when idle maintenance should be stopped.
+     * This means that the user was not interacting with the device as a result
+     * of which a broadcast with action {@link #ACTION_IDLE_MAINTENANCE_START}
+     * was sent and now the user started interacting with the device. Typical
+     * use of the idle maintenance is to perform somehow expensive tasks that
+     * can be postponed at a moment when they will not degrade user experience.
+     * <p>
+     * <p class="note">In order to keep the device responsive in case of an
+     * unexpected user interaction, implementations of a maintenance task
+     * should be interruptible. Hence, on receiving a broadcast with this
+     * action, the maintenance task should be interrupted as soon as possible.
+     * In other words, you should not do the maintenance work in
+     * {@link BroadcastReceiver#onReceive(Context, Intent)}, rather stop the
+     * maintenance service that was started on receiving of
+     * {@link #ACTION_IDLE_MAINTENANCE_START}.Also you should release the wake
+     * lock you acquired when your maintenance service started.
+     * </p>
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @see #ACTION_IDLE_MAINTENANCE_START
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_IDLE_MAINTENANCE_END =
+            "android.intent.action.ACTION_IDLE_MAINTENANCE_END";
+
+    /**
      * Broadcast Action: a remote intent is to be broadcasted.
      *
      * A remote intent is used for remote RPC between devices. The remote intent
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 9c44737..7ff8d09 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -937,6 +937,8 @@
 
     @Override
     public void drawLines(float[] pts, int offset, int count, Paint paint) {
+        if (count < 4) return;
+
         if ((offset | count) < 0 || offset + count > pts.length) {
             throw new IllegalArgumentException("The lines array must contain 4 elements per line.");
         }
@@ -1060,6 +1062,8 @@
 
     @Override
     public void drawPoints(float[] pts, int offset, int count, Paint paint) {
+        if (count < 2) return;
+
         int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
         try {
             nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d77b504..8a53cc3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -170,6 +170,9 @@
     <protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" />
     <protected-broadcast android:name="android.intent.action.ADVANCED_SETTINGS" />
 
+    <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
+    <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_END" />
+
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
     <!-- ====================================== -->
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_dark.9.png
new file mode 100644
index 0000000..57141d5
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_light.9.png
new file mode 100644
index 0000000..8e28906
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_dark.9.png
new file mode 100644
index 0000000..efdab73
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_light.9.png
new file mode 100644
index 0000000..307b86d
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_dark.9.png
new file mode 100644
index 0000000..2e28431
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_light.9.png
new file mode 100644
index 0000000..7a83451
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_dark.9.png
new file mode 100644
index 0000000..9afc912
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_light.9.png
new file mode 100644
index 0000000..7d03855
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_dark.9.png
new file mode 100644
index 0000000..21f1e2c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_light.9.png
new file mode 100644
index 0000000..600b861
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_dark.9.png
new file mode 100644
index 0000000..c11619c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_light.9.png
new file mode 100644
index 0000000..91be10f
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_dark.9.png
new file mode 100644
index 0000000..c921487
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_light.9.png
new file mode 100644
index 0000000..4da6c6e
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_dark.9.png
new file mode 100644
index 0000000..9c7d294
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_light.9.png
new file mode 100644
index 0000000..c9f493a
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_dark.9.png
new file mode 100644
index 0000000..02c799e
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_light.9.png
new file mode 100644
index 0000000..286157c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_dark.9.png
new file mode 100644
index 0000000..030f723
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_light.9.png
new file mode 100644
index 0000000..a5b10d2
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_dark.9.png
new file mode 100644
index 0000000..10faec10
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_light.9.png
new file mode 100644
index 0000000..62a70ed
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_dark.9.png
new file mode 100644
index 0000000..633b8d2
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_light.9.png
new file mode 100644
index 0000000..59ff556
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_dark.9.png
new file mode 100644
index 0000000..09d5aa4
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_light.9.png
new file mode 100644
index 0000000..c320ea0
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_dark.9.png
new file mode 100644
index 0000000..a5f28fd
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_light.9.png
new file mode 100644
index 0000000..9e0d39c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_dark.9.png
new file mode 100644
index 0000000..24d928f
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_light.9.png
new file mode 100644
index 0000000..596ba2d
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_dark.9.png
new file mode 100644
index 0000000..55b1af7
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_light.9.png
new file mode 100644
index 0000000..bb0486a
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_dark.9.png
new file mode 100644
index 0000000..6f41b24
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_light.9.png
new file mode 100644
index 0000000..5a96fc1
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_dark.9.png
new file mode 100644
index 0000000..96a6da5
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_light.9.png
new file mode 100644
index 0000000..849d795
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_dark.9.png
new file mode 100644
index 0000000..3d6d0f9
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_light.9.png
new file mode 100644
index 0000000..1cbaf6c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_dark.9.png
new file mode 100644
index 0000000..878e90d
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_light.9.png
new file mode 100644
index 0000000..f25acc2
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_dark.9.png
new file mode 100644
index 0000000..8d89c86
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_light.9.png
new file mode 100644
index 0000000..6e3ca08
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_dark.9.png
new file mode 100644
index 0000000..2204091
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_light.9.png
new file mode 100644
index 0000000..3e6684e
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_dark.9.png
new file mode 100644
index 0000000..5129dee
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_light.9.png
new file mode 100644
index 0000000..0f0289b
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_dark.9.png
new file mode 100644
index 0000000..795820b
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_light.9.png
new file mode 100644
index 0000000..830edfd
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_light.9.png
Binary files differ
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 8cbf299..e94ddae 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1199,7 +1199,7 @@
      * meshHeight+1 vertices down. The verts array is accessed in row-major
      * order, so that the first meshWidth+1 vertices are distributed across the
      * top of the bitmap from left to right. A more general version of this
-     * methid is drawVertices().
+     * method is drawVertices().
      *
      * @param bitmap The bitmap to draw using the mesh
      * @param meshWidth The number of columns in the mesh. Nothing is drawn if
@@ -1208,7 +1208,7 @@
      *                   this is 0
      * @param verts Array of x,y pairs, specifying where the mesh should be
      *              drawn. There must be at least
-     *              (meshWidth+1) * (meshHeight+1) * 2 + meshOffset values
+     *              (meshWidth+1) * (meshHeight+1) * 2 + vertOffset values
      *              in the array
      * @param vertOffset Number of verts elements to skip before drawing
      * @param colors May be null. Specifies a color at each vertex, which is
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 6fab8da..398f719 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -436,13 +436,8 @@
         Caches::getInstance().eventMark(strlen(op->name()), op->name());
 #endif
 
-        if (deferredList) {
-            drawGlStatus |= op->replay(renderer, dirty, flags,
-                    saveCount, level, mCaching, mMultipliedAlpha, *deferredList);
-        } else {
-            drawGlStatus |= op->replay(renderer, dirty, flags,
-                    saveCount, level, mCaching, mMultipliedAlpha);
-        }
+        drawGlStatus |= op->replay(renderer, dirty, flags,
+                saveCount, level, mCaching, mMultipliedAlpha, deferredList);
         logBuffer.writeCommand(level, op->name());
     }
 
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 8e80647..1bae0ff 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -74,14 +74,11 @@
         kOpLogFlag_JSON = 0x2 // TODO: add?
     };
 
-    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
-            uint32_t level, bool caching, int multipliedAlpha) = 0;
-
-    // same as replay above, but draw operations will defer into the deferredList if possible
-    // NOTE: colorfilters, paintfilters, shaders, shadow, and complex clips prevent deferral
+    // If a DeferredDisplayList is supplied, DrawOps will be stored until the list is flushed
+    // NOTE: complex clips and layers prevent deferral
     virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
             uint32_t level, bool caching, int multipliedAlpha,
-            DeferredDisplayList& deferredList) = 0;
+            DeferredDisplayList* deferredList) = 0;
 
     virtual void output(int level, uint32_t flags = 0) = 0;
 
@@ -96,22 +93,16 @@
 
     virtual ~StateOp() {}
 
-    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
-            uint32_t level, bool caching, int multipliedAlpha) {
-        applyState(renderer, saveCount);
-        return DrawGlInfo::kStatusDone;
-    }
-
     /**
      * State operations are applied directly to the renderer, but can cause the deferred drawing op
      * list to flush
      */
     virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
-            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList& deferredList) {
+            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList* deferredList) {
         status_t status = DrawGlInfo::kStatusDone;
-        if (requiresDrawOpFlush()) {
+        if (deferredList && requiresDrawOpFlush()) {
             // will be setting renderer state that affects ops in deferredList, so flush list first
-            status |= deferredList.flush(renderer, dirty, flags, level);
+            status |= deferredList->flush(renderer, dirty, flags, level);
         }
         applyState(renderer, saveCount);
         return status;
@@ -131,23 +122,14 @@
     DrawOp(SkPaint* paint)
             : mPaint(paint), mQuickRejected(false) {}
 
-    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
-            uint32_t level, bool caching, int multipliedAlpha) {
-        if (mQuickRejected && CC_LIKELY(flags & DisplayList::kReplayFlag_ClipChildren)) {
-            return DrawGlInfo::kStatusDone;
-        }
-
-        return applyDraw(renderer, dirty, level, caching, multipliedAlpha);
-    }
-
     /** Draw operations are stored in the deferredList with information necessary for playback */
     virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
-            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList& deferredList) {
+            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList* deferredList) {
         if (mQuickRejected && CC_LIKELY(flags & DisplayList::kReplayFlag_ClipChildren)) {
             return DrawGlInfo::kStatusDone;
         }
 
-        if (renderer.disallowDeferral()) {
+        if (!deferredList || renderer.disallowDeferral()) {
             // dispatch draw immediately, since the renderer's state is too complex for deferral
             return applyDraw(renderer, dirty, level, caching, multipliedAlpha);
         }
@@ -161,7 +143,7 @@
 
         if (!renderer.storeDisplayState(state)) {
             // op wasn't quick-rejected, so defer
-            deferredList.add(this, renderer.disallowReorder());
+            deferredList->add(this, renderer.disallowReorder());
         }
 
         return DrawGlInfo::kStatusDone;
@@ -184,7 +166,6 @@
 
     float strokeWidthOutset() { return mPaint->getStrokeWidth() * 0.5f; }
 
-public:
     /**
      * Stores the relevant canvas state of the object between deferral and replay (if the canvas
      * state supports being stored) See OpenGLRenderer::simpleClipAndState()
@@ -204,7 +185,20 @@
     DrawBoundedOp(float left, float top, float right, float bottom, SkPaint* paint)
             : DrawOp(paint), mLocalBounds(left, top, right, bottom) {}
 
-    // default constructor for area, to be overridden in child constructor body
+    // Calculates bounds as smallest rect encompassing all points
+    // NOTE: requires at least 1 vertex, and doesn't account for stroke size (should be handled in
+    // subclass' constructor)
+    DrawBoundedOp(const float* points, int count, SkPaint* paint)
+            : DrawOp(paint), mLocalBounds(points[0], points[1], points[0], points[1]) {
+        for (int i = 2; i < count; i += 2) {
+            mLocalBounds.left = fminf(mLocalBounds.left, points[i]);
+            mLocalBounds.right = fmaxf(mLocalBounds.right, points[i]);
+            mLocalBounds.top = fminf(mLocalBounds.top, points[i + 1]);
+            mLocalBounds.bottom = fmaxf(mLocalBounds.bottom, points[i + 1]);
+        }
+    }
+
+    // default empty constructor for bounds, to be overridden in child constructor body
     DrawBoundedOp(SkPaint* paint)
             : DrawOp(paint) {}
 
@@ -740,11 +734,12 @@
     }
 };
 
-class DrawBitmapMeshOp : public DrawOp {
+class DrawBitmapMeshOp : public DrawBoundedOp {
 public:
     DrawBitmapMeshOp(SkBitmap* bitmap, int meshWidth, int meshHeight,
             float* vertices, int* colors, SkPaint* paint)
-            : DrawOp(paint), mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight),
+            : DrawBoundedOp(vertices, 2 * (meshWidth + 1) * (meshHeight + 1), paint),
+            mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight),
             mVertices(vertices), mColors(colors) {}
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
@@ -873,10 +868,11 @@
     virtual const char* name() { return "DrawRect"; }
 };
 
-class DrawRectsOp : public DrawOp {
+class DrawRectsOp : public DrawBoundedOp {
 public:
     DrawRectsOp(const float* rects, int count, SkPaint* paint)
-            : DrawOp(paint), mRects(rects), mCount(count) {}
+            : DrawBoundedOp(rects, count, paint),
+            mRects(rects), mCount(count) {}
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
             bool caching, int multipliedAlpha) {
@@ -1022,13 +1018,8 @@
 class DrawLinesOp : public DrawBoundedOp {
 public:
     DrawLinesOp(float* points, int count, SkPaint* paint)
-            : DrawBoundedOp(paint), mPoints(points), mCount(count) {
-        for (int i = 0; i < count; i += 2) {
-            mLocalBounds.left = fminf(mLocalBounds.left, points[i]);
-            mLocalBounds.right = fmaxf(mLocalBounds.right, points[i]);
-            mLocalBounds.top = fminf(mLocalBounds.top, points[i+1]);
-            mLocalBounds.bottom = fmaxf(mLocalBounds.bottom, points[i+1]);
-        }
+            : DrawBoundedOp(points, count, paint),
+            mPoints(points), mCount(count) {
         mLocalBounds.outset(strokeWidthOutset());
     }
 
@@ -1199,26 +1190,23 @@
     Functor* mFunctor;
 };
 
-class DrawDisplayListOp : public DrawOp {
+class DrawDisplayListOp : public DrawBoundedOp {
 public:
     DrawDisplayListOp(DisplayList* displayList, int flags)
-            : DrawOp(0), mDisplayList(displayList), mFlags(flags) {}
+            : DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0),
+            mDisplayList(displayList), mFlags(flags) {}
 
     virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
-            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList& deferredList) {
+            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList* deferredList) {
         if (mDisplayList && mDisplayList->isRenderable()) {
-            return mDisplayList->replay(renderer, dirty, mFlags, level + 1, &deferredList);
+            return mDisplayList->replay(renderer, dirty, mFlags, level + 1, deferredList);
         }
         return DrawGlInfo::kStatusDone;
     }
 
+    // NOT USED, since replay is overridden
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
-            bool caching, int multipliedAlpha) {
-        if (mDisplayList && mDisplayList->isRenderable()) {
-            return mDisplayList->replay(renderer, dirty, mFlags, level + 1);
-        }
-        return DrawGlInfo::kStatusDone;
-    }
+            bool caching, int multipliedAlpha) { return DrawGlInfo::kStatusDone; }
 
     virtual void output(int level, uint32_t flags) {
         OP_LOG("Draw Display List %p, flags %#x", mDisplayList, mFlags);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index e5fd7b9..6b614a7 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1235,6 +1235,8 @@
             // quick rejected
             return true;
         }
+    } else {
+        state.mBounds.set(currentClip);
     }
 
     state.mClip.set(currentClip);
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 4398642..f6593e0 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -191,17 +191,27 @@
         public static final int AACObjectHE_PS      = 29;
         public static final int AACObjectELD        = 39;
 
+        // from OMX_VIDEO_VP8LEVELTYPE
+        public static final int VP8Level_Version0 = 0x01;
+        public static final int VP8Level_Version1 = 0x02;
+        public static final int VP8Level_Version2 = 0x04;
+        public static final int VP8Level_Version3 = 0x08;
+
+        // from OMX_VIDEO_VP8PROFILETYPE
+        public static final int VP8ProfileMain = 0x01;
+
+
         /**
          * Defined in the OpenMAX IL specs, depending on the type of media
          * this can be OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263PROFILETYPE,
-         * or OMX_VIDEO_MPEG4PROFILETYPE.
+         * OMX_VIDEO_MPEG4PROFILETYPE or OMX_VIDEO_VP8PROFILETYPE.
          */
         public int profile;
 
         /**
          * Defined in the OpenMAX IL specs, depending on the type of media
          * this can be OMX_VIDEO_AVCLEVELTYPE, OMX_VIDEO_H263LEVELTYPE
-         * or OMX_VIDEO_MPEG4LEVELTYPE.
+         * OMX_VIDEO_MPEG4LEVELTYPE or OMX_VIDEO_VP8LEVELTYPE.
          */
         public int level;
     };
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 79f9650..8d949a5 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -485,14 +485,12 @@
 
             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 
-            if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
-                throw new RuntimeException("Cannot swap buffers");
-            }
+            boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
             checkEglError();
 
             finishGL();
 
-            return true;
+            return status;
         }
 
         private FloatBuffer createMesh(int left, int top, float right, float bottom) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 4e5825e..acbde9b 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -313,6 +313,9 @@
     // bar can be hidden but not extending into the overscan area.
     int mUnrestrictedScreenLeft, mUnrestrictedScreenTop;
     int mUnrestrictedScreenWidth, mUnrestrictedScreenHeight;
+    // Like mOverscanScreen*, but allowed to move into the overscan region where appropriate.
+    int mRestrictedOverscanScreenLeft, mRestrictedOverscanScreenTop;
+    int mRestrictedOverscanScreenWidth, mRestrictedOverscanScreenHeight;
     // The current size of the screen; these may be different than (0,0)-(dw,dh)
     // if the status bar can't be hidden; in that case it effectively carves out
     // that area of the display from all other windows.
@@ -2415,10 +2418,10 @@
             overscanRight = 0;
             overscanBottom = 0;
         }
-        mOverscanScreenLeft = 0;
-        mOverscanScreenTop = 0;
-        mOverscanScreenWidth = displayWidth;
-        mOverscanScreenHeight = displayHeight;
+        mOverscanScreenLeft = mRestrictedOverscanScreenLeft = 0;
+        mOverscanScreenTop = mRestrictedOverscanScreenTop = 0;
+        mOverscanScreenWidth = mRestrictedOverscanScreenWidth = displayWidth;
+        mOverscanScreenHeight = mRestrictedOverscanScreenHeight = displayHeight;
         mSystemLeft = 0;
         mSystemTop = 0;
         mSystemRight = displayWidth;
@@ -2492,7 +2495,8 @@
                     if (navVisible) {
                         mNavigationBar.showLw(true);
                         mDockBottom = mTmpNavigationFrame.top;
-                        mRestrictedScreenHeight = mDockBottom - mDockTop;
+                        mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
+                        mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
                     } else {
                         // We currently want to hide the navigation UI.
                         mNavigationBar.hideLw(true);
@@ -2512,7 +2516,8 @@
                     if (navVisible) {
                         mNavigationBar.showLw(true);
                         mDockRight = mTmpNavigationFrame.left;
-                        mRestrictedScreenWidth = mDockRight - mDockLeft;
+                        mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
+                        mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
                     } else {
                         // We currently want to hide the navigation UI.
                         mNavigationBar.hideLw(true);
@@ -2776,15 +2781,17 @@
                         // application extend into the unrestricted overscan screen area.  We
                         // only do this for application windows to ensure no window that
                         // can be above the nav bar can do this.
-                        pf.left = df.left = mUnrestrictedScreenLeft;
-                        pf.top = df.top = mUnrestrictedScreenTop;
-                        pf.right = df.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
-                        pf.bottom = df.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+                        pf.left = df.left = mOverscanScreenLeft;
+                        pf.top = df.top = mOverscanScreenTop;
+                        pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
+                        pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
                     } else {
-                        pf.left = df.left = mRestrictedScreenLeft;
-                        pf.top = df.top = mRestrictedScreenTop;
-                        pf.right = df.right = mRestrictedScreenLeft+mRestrictedScreenWidth;
-                        pf.bottom = df.bottom = mRestrictedScreenTop+mRestrictedScreenHeight;
+                        pf.left = df.left = mRestrictedOverscanScreenLeft;
+                        pf.top = df.top = mRestrictedOverscanScreenTop;
+                        pf.right = df.right = mRestrictedOverscanScreenLeft
+                                + mRestrictedOverscanScreenWidth;
+                        pf.bottom = df.bottom = mRestrictedOverscanScreenTop
+                                + mRestrictedOverscanScreenHeight;
                     }
 
                     if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
@@ -2859,6 +2866,14 @@
                     pf.right = df.right = cf.right = mOverscanScreenLeft + mOverscanScreenWidth;
                     pf.bottom = df.bottom = cf.bottom
                             = mOverscanScreenTop + mOverscanScreenHeight;
+                } else if (attrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER) {
+                    // The wallpaper mostly goes into the overscan region.
+                    pf.left = df.left = cf.left = mRestrictedOverscanScreenLeft;
+                    pf.top = df.top = cf.top = mRestrictedOverscanScreenTop;
+                    pf.right = df.right = cf.right
+                            = mRestrictedOverscanScreenLeft + mRestrictedOverscanScreenWidth;
+                    pf.bottom = df.bottom = cf.bottom
+                            = mRestrictedOverscanScreenTop + mRestrictedOverscanScreenHeight;
                 } else if ((attrs.flags & FLAG_LAYOUT_IN_OVERSCAN) != 0
                         && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
                         && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
@@ -4712,6 +4727,11 @@
                     pw.print(" right="); pw.print(mOverscanRight);
                     pw.print(" bottom="); pw.println(mOverscanBottom);
         }
+        pw.print(prefix); pw.print("mRestrictedOverscanScreen=(");
+                pw.print(mRestrictedOverscanScreenLeft);
+                pw.print(","); pw.print(mRestrictedOverscanScreenTop);
+                pw.print(") "); pw.print(mRestrictedOverscanScreenWidth);
+                pw.print("x"); pw.println(mRestrictedOverscanScreenHeight);
         pw.print(prefix); pw.print("mUnrestrictedScreen=("); pw.print(mUnrestrictedScreenLeft);
                 pw.print(","); pw.print(mUnrestrictedScreenTop);
                 pw.print(") "); pw.print(mUnrestrictedScreenWidth);
diff --git a/services/java/com/android/server/IdleMaintenanceService.java b/services/java/com/android/server/IdleMaintenanceService.java
new file mode 100644
index 0000000..a7442f6
--- /dev/null
+++ b/services/java/com/android/server/IdleMaintenanceService.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.Log;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * This service observes the device state and when applicable sends
+ * broadcasts at the beginning and at the end of a period during which
+ * observers can perform idle maintenance tasks. Typical use of the
+ * idle maintenance is to perform somehow expensive tasks that can be
+ * postponed to a moment when they will not degrade user experience.
+ *
+ * The current implementation is very simple. The start of a maintenance
+ * window is announced if: the screen is off or showing a dream AND the
+ * battery level is more than twenty percent AND at least one hour passed
+ * since the screen went off or a dream started (i.e. since the last user
+ * activity).
+ *
+ * The end of a maintenance window is announced only if: a start was
+ * announced AND the screen turned on or a dream was stopped.
+ */
+public class IdleMaintenanceService extends BroadcastReceiver {
+
+    private final boolean DEBUG = false;
+
+    private static final String LOG_TAG = IdleMaintenanceService.class.getSimpleName();
+
+    private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
+
+    private static final int MIN_IDLE_MAINTENANCE_START_BATTERY_LEVEL = 20; // percent
+
+    private static final long MIN_IDLE_MAINTENANCE_START_USER_INACTIVITY = 60 * 60 * 1000; // 1 hour
+
+    private final Intent mIdleMaintenanceStartIntent =
+            new Intent(Intent.ACTION_IDLE_MAINTENANCE_START);
+
+    private final Intent mIdleMaintenanceEndIntent =
+            new Intent(Intent.ACTION_IDLE_MAINTENANCE_END);
+
+    private final Context mContext;
+
+    private final WakeLock mWakeLock;
+
+    private final Handler mHandler;
+
+    private final Calendar mTempCalendar = Calendar.getInstance();
+
+    private final Calendar mLastIdleMaintenanceStartTime = Calendar.getInstance();
+
+    private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
+
+    private int mBatteryLevel;
+
+    private boolean mIdleMaintenanceStarted;
+
+    public IdleMaintenanceService(Context context) {
+        mContext = context;
+
+        PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
+
+        mHandler = new Handler(mContext.getMainLooper());
+
+        // Move one day back so we can run maintenance the first day after starting.
+        final int prevDayOfYear = mLastIdleMaintenanceStartTime.get(Calendar.DAY_OF_YEAR) - 1;
+        mLastIdleMaintenanceStartTime.set(Calendar.DAY_OF_YEAR, prevDayOfYear);
+
+        register(mContext.getMainLooper());
+    }
+
+    public void register(Looper looper) {
+        IntentFilter intentFilter = new IntentFilter();
+
+        // Battery actions.
+        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
+
+        // Screen actions.
+        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+
+        // Dream actions.
+        intentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
+        intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
+
+        mContext.registerReceiverAsUser(this, UserHandle.ALL,
+                intentFilter, null, new Handler(looper));
+    }
+
+    private void updateIdleMaintenanceState() {
+        if (mIdleMaintenanceStarted) {
+            // Idle maintenance can be interrupted only by
+            // a change of the device state.
+            if (!deviceStatePermitsIdleMaintenance()) {
+                mIdleMaintenanceStarted = false;
+                sendIdleMaintenanceEndIntent();
+            }
+        } else if (deviceStatePermitsIdleMaintenance()
+                && lastUserActivityPermitsIdleMaintenanceStart()
+                && lastRunPermitsIdleMaintenanceStart()) {
+            mIdleMaintenanceStarted = true;
+            mLastIdleMaintenanceStartTime.setTimeInMillis(System.currentTimeMillis());
+            sendIdleMaintenanceStartIntent();
+        }
+    }
+
+    private void sendIdleMaintenanceStartIntent() {
+        if (DEBUG) {
+            Log.i(LOG_TAG, "Broadcasting " + Intent.ACTION_IDLE_MAINTENANCE_START);
+        }
+        mWakeLock.acquire();
+        mContext.sendOrderedBroadcastAsUser(mIdleMaintenanceStartIntent, UserHandle.ALL,
+                null, this, mHandler, Activity.RESULT_OK, null, null);
+    }
+
+    private void sendIdleMaintenanceEndIntent() {
+        if (DEBUG) {
+            Log.i(LOG_TAG, "Broadcasting " + Intent.ACTION_IDLE_MAINTENANCE_END);
+        }
+        mWakeLock.acquire();
+        mContext.sendOrderedBroadcastAsUser(mIdleMaintenanceEndIntent, UserHandle.ALL,
+                null, this, mHandler, Activity.RESULT_OK, null, null);
+    }
+
+    private boolean deviceStatePermitsIdleMaintenance() {
+        return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
+                && mBatteryLevel > MIN_IDLE_MAINTENANCE_START_BATTERY_LEVEL);
+    }
+
+    private boolean lastUserActivityPermitsIdleMaintenanceStart() {
+        return (SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
+                > MIN_IDLE_MAINTENANCE_START_USER_INACTIVITY);
+    }
+
+    private boolean lastRunPermitsIdleMaintenanceStart() {
+        Calendar now = mTempCalendar;
+        // Not setting the Locale since we do not care of locale
+        // specific properties such as the first day of the week.
+        now.setTimeZone(TimeZone.getDefault());
+        now.setTimeInMillis(System.currentTimeMillis());
+
+        Calendar lastRun = mLastIdleMaintenanceStartTime;
+        // Not setting the Locale since we do not care of locale
+        // specific properties such as the first day of the week.
+        lastRun.setTimeZone(TimeZone.getDefault());
+
+        return now.get(Calendar.DAY_OF_YEAR) != lastRun.get(Calendar.DAY_OF_YEAR);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (DEBUG) {
+            Log.i(LOG_TAG, intent.getAction());
+        }
+        String action = intent.getAction();
+        if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+            final int maxBatteryLevel = intent.getExtras().getInt(BatteryManager.EXTRA_SCALE);
+            final int currBatteryLevel = intent.getExtras().getInt(BatteryManager.EXTRA_LEVEL);
+            mBatteryLevel = (int) (((float) maxBatteryLevel / 100) * currBatteryLevel);
+        } else if (Intent.ACTION_SCREEN_ON.equals(action)
+                || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
+            mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
+        } else if (Intent.ACTION_SCREEN_OFF.equals(action)
+                || Intent.ACTION_DREAMING_STARTED.equals(action)) {
+            mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
+        } else if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)
+                || Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
+            mWakeLock.release();
+            return;
+        }
+        updateIdleMaintenanceState();
+    }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 39ee47e..949c2ed 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -255,7 +255,7 @@
             }
 
             ActivityManagerService.setSystemProcess();
-            
+
             Slog.i(TAG, "User Service");
             ServiceManager.addService(Context.USER_SERVICE,
                     UserManagerService.getInstance());
@@ -736,6 +736,13 @@
                     reportWtf("starting DreamManagerService", e);
                 }
             }
+
+            try {
+                Slog.i(TAG, "IdleMaintenanceService");
+                new IdleMaintenanceService(context);
+            } catch (Throwable e) {
+                reportWtf("starting IdleMaintenanceService", e);
+            }
         }
 
         // Before things start rolling, be sure we have decided whether
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index c1b406e..f2d401f 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -471,6 +471,7 @@
     void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) {
         if (inHistory && !finishing) {
             if (task != null) {
+                task.removeActivity(this);
                 task.numActivities--;
             }
             if (newTask != null) {
@@ -505,6 +506,7 @@
             inHistory = false;
             if (task != null && !finishing) {
                 task.numActivities--;
+                task = null;
             }
             clearOptionsLocked();
         }
@@ -539,7 +541,7 @@
         ActivityResult r = new ActivityResult(from, resultWho,
         		requestCode, resultCode, resultData);
         if (results == null) {
-            results = new ArrayList();
+            results = new ArrayList<ResultInfo>();
         }
         results.add(r);
     }
@@ -950,6 +952,8 @@
         StringBuilder sb = new StringBuilder(128);
         sb.append("ActivityRecord{");
         sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(" t");
+        sb.append(task.taskId);
         sb.append(" u");
         sb.append(userId);
         sb.append(' ');
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index c54cdaa..a18a0d1 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -54,6 +54,7 @@
 import android.graphics.Bitmap.Config;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -66,6 +67,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.Display;
 
 import java.io.FileDescriptor;
@@ -75,6 +77,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.NoSuchElementException;
 
 /**
  * State and management of a single stack of activities.
@@ -98,7 +101,8 @@
     static final boolean DEBUG_APP = false;
 
     static final boolean VALIDATE_TOKENS = ActivityManagerService.VALIDATE_TOKENS;
-    
+    static final boolean VALIDATE_TASK_REPLACE = true;
+
     // How long we wait until giving up on the last activity telling us it
     // is idle.
     static final int IDLE_TIMEOUT = 10*1000;
@@ -137,7 +141,10 @@
     // Set to false to disable the preview that is shown while a new activity
     // is being started.
     static final boolean SHOW_APP_STARTING_PREVIEW = true;
-    
+
+    static final boolean FORWARD_ITERATOR = false;
+    static final boolean REVERSE_ITERATOR = true;
+
     enum ActivityState {
         INITIALIZING,
         RESUMED,
@@ -154,7 +161,7 @@
     final boolean mMainStack;
     
     final Context mContext;
-    
+
     /**
      * The back history of all previous (and possibly still
      * running) activities.  It contains #ActivityRecord objects.
@@ -162,6 +169,17 @@
     private final ArrayList<ActivityRecord> mHistory = new ArrayList<ActivityRecord>();
 
     /**
+     * The back history of all previous (and possibly still
+     * running) activities.  It contains #TaskRecord objects.
+     */
+    private ArrayList<TaskRecord> mTaskHistory = new ArrayList<TaskRecord>();
+
+    /**
+     * Mapping from taskId to TaskRecord
+     */
+    private SparseArray<TaskRecord> mTaskIdToTaskRecord = new SparseArray<TaskRecord>();
+
+    /**
      * Used for validating app tokens with window manager.
      */
     final ArrayList<TaskGroup> mValidateAppTokens = new ArrayList<TaskGroup>();
@@ -289,6 +307,12 @@
      */
     boolean mDismissKeyguardOnNextActivity = false;
 
+    /** So we don't have to keep constructing a new object for utility non-nested use. */
+    final ActivityIterator mTmpActivityIterator = new ActivityIterator(FORWARD_ITERATOR, true);
+
+    /** So we don't have to keep constructing a new object for utility non-nested use. */
+    final TaskIterator mTmpTaskIterator = new TaskIterator();
+
     /**
      * Save the most recent screenshot for reuse. This keeps Recents from taking two identical
      * screenshots, one for the Recents thumbnail and one for the pauseActivity thumbnail.
@@ -508,30 +532,69 @@
     }
 
     final ActivityRecord isInStackLocked(IBinder token) {
+        ActivityRecord newAr = newIsInStackLocked(token);
+
         ActivityRecord r = ActivityRecord.forToken(token);
         if (mHistory.contains(r)) {
+            if (VALIDATE_TASK_REPLACE && newAr != r) Slog.w(TAG,
+                    "isInStackLocked: mismatch: newAr=" + newAr + " r=" + r);
             return r;
         }
+        if (VALIDATE_TASK_REPLACE && newAr != null) Slog.w(TAG,
+                "isInStackLocked: mismatch: newAr!=null");
+        return null;
+    }
+
+    final ActivityRecord newIsInStackLocked(IBinder token) {
+        final ActivityRecord r = ActivityRecord.forToken(token);
+        if (r != null) {
+            final TaskRecord task = r.task;
+            if (mTaskHistory.contains(task) && task.mActivities.contains(r)) {
+                return r;
+            }
+        }
         return null;
     }
 
     int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
+        int newTaskId = newGetTaskForActivityLocked(token, onlyRoot);
+
         TaskRecord lastTask = null;
         final int N = mHistory.size();
         for (int i = 0; i < N; i++) {
             ActivityRecord r = mHistory.get(i);
             if (r.appToken == token) {
                 if (!onlyRoot || lastTask != r.task) {
+                    if (VALIDATE_TASK_REPLACE && newTaskId != r.task.taskId) Slog.w(TAG,
+                            "getTaskForActivityLocked: mismatch: new=" + newTaskId
+                            + " taskId=" + r.task.taskId);
                     return r.task.taskId;
                 }
+                if (VALIDATE_TASK_REPLACE && newTaskId != -1) Slog.w(TAG,
+                    "getTaskForActivityLocked: mismatch: newTaskId=" + newTaskId + " not -1.");
                 return -1;
             }
             lastTask = r.task;
         }
 
+        if (VALIDATE_TASK_REPLACE && newTaskId != -1) Slog.w(TAG,
+            "getTaskForActivityLocked: mismatch at end: newTaskId=" + newTaskId + " not -1.");
         return -1;
     }
 
+    int newGetTaskForActivityLocked(IBinder token, boolean onlyRoot) {
+        final ActivityRecord r = ActivityRecord.forToken(token);
+        if (r == null) {
+            return -1;
+        }
+        final TaskRecord task = r.task;
+        switch (task.mActivities.indexOf(r)) {
+            case -1: return -1;
+            case 0: return task.taskId;
+            default: return onlyRoot ? -1 : task.taskId;
+        }
+    }
+
     private final boolean updateLRUListLocked(ActivityRecord r) {
         final boolean hadit = mLRUActivities.remove(r);
         mLRUActivities.add(r);
@@ -624,17 +687,29 @@
      * @return whether there are any activities for the specified user.
      */
     final boolean switchUserLocked(int userId, UserStartedState uss) {
+        if (VALIDATE_TOKENS) {
+            validateAppTokensLocked();
+        }
+        final boolean newResult = newSwitchUserLocked(userId, uss);
+
         mCurrentUser = userId;
         mStartingUsers.add(uss);
 
         // Only one activity? Nothing to do...
-        if (mHistory.size() < 2)
+        if (mHistory.size() < 2) {
+            if (VALIDATE_TASK_REPLACE && newResult) Slog.w(TAG,
+                    "switchUserLocked: mismatch: " + newResult + " " + false);
             return false;
+        }
 
         boolean haveActivities = false;
         // Check if the top activity is from the new user.
         ActivityRecord top = mHistory.get(mHistory.size() - 1);
-        if (top.userId == userId) return true;
+        if (top.userId == userId) {
+            if (VALIDATE_TASK_REPLACE && !newResult) Slog.w(TAG,
+                    "switchUserLocked: mismatch: " + newResult + " " + true);
+            return true;
+        }
         // Otherwise, move the user's activities to the top.
         int N = mHistory.size();
         int i = 0;
@@ -651,7 +726,44 @@
             }
         }
         // Transition from the old top to the new top
+        if (VALIDATE_TASK_REPLACE) Slog.w(TAG,
+                "switchUserLocked: calling resumeTopActivity " + top);
         resumeTopActivityLocked(top);
+        if (VALIDATE_TASK_REPLACE && (newResult != haveActivities)) Slog.w(TAG,
+                    "switchUserLocked: mismatch: " + newResult + " " + haveActivities);
+        return haveActivities;
+    }
+
+    /*
+     * Move the activities around in the stack to bring a user to the foreground.
+     * @return whether there are any activities for the specified user.
+     */
+    final boolean newSwitchUserLocked(int userId, UserStartedState uss) {
+//        mStartingUsers.add(uss);
+        if (mCurrentUser == userId) {
+            return true;
+        }
+        mCurrentUser = userId;
+
+        // Move userId's tasks to the top.
+        boolean haveActivities = false;
+        TaskRecord task = null;
+        int index = mTaskHistory.size();
+        for (int i = 0; i < index; ++i) {
+            task = mTaskHistory.get(i);
+            if (task.userId == userId) {
+                haveActivities = true;
+                mTaskHistory.remove(i);
+                mTaskHistory.add(task);
+                --index;
+            }
+        }
+
+        // task is now the original topmost TaskRecord. Transition from the old top to the new top.
+        ActivityRecord top = task != null ? task.getTopActivity() : null;
+        if (VALIDATE_TASK_REPLACE) Slog.w(TAG,
+                "newSwitchUserLocked: would call resumeTopActivity " + top);
+//        resumeTopActivityLocked(top);
         return haveActivities;
     }
 
@@ -889,6 +1001,9 @@
             mGoingToSleep.release();
         }
         // Ensure activities are no longer sleeping.
+        if (VALIDATE_TASK_REPLACE) {
+            verifyActivityRecords();
+        }
         for (int i=mHistory.size()-1; i>=0; i--) {
             ActivityRecord r = mHistory.get(i);
             r.setSleeping(false);
@@ -933,6 +1048,9 @@
 
             // Make sure any stopped but visible activities are now sleeping.
             // This ensures that the activity's onStop() is called.
+            if (VALIDATE_TASK_REPLACE) {
+                verifyActivityRecords();
+            }
             for (int i=mHistory.size()-1; i>=0; i--) {
                 ActivityRecord r = mHistory.get(i);
                 if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) {
@@ -1077,6 +1195,9 @@
         ActivityRecord r = null;
 
         synchronized (mService) {
+            if (VALIDATE_TASK_REPLACE) {
+                verifyActivityRecords();
+            }
             int index = indexOfTokenLocked(token);
             if (index >= 0) {
                 r = mHistory.get(index);
@@ -1094,6 +1215,9 @@
         ActivityRecord r = null;
 
         synchronized (mService) {
+            if (VALIDATE_TASK_REPLACE) {
+                verifyActivityRecords();
+            }
             int index = indexOfTokenLocked(token);
             if (index >= 0) {
                 r = mHistory.get(index);
@@ -1306,6 +1430,9 @@
 
         // If the top activity is not fullscreen, then we need to
         // make sure any activities under it are now visible.
+        if (VALIDATE_TASK_REPLACE) {
+            verifyActivityRecords();
+        }
         final int count = mHistory.size();
         int i = count-1;
         while (mHistory.get(i) != top) {
@@ -1868,6 +1995,7 @@
                             Slog.i(TAG, "Adding activity " + r + " to stack at " + addPos,
                                     here);
                         }
+                        r.task.addActivityToTop(r);
                         mHistory.add(addPos, r);
                         r.putInHistory();
                         mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId,
@@ -1907,6 +2035,7 @@
             here.fillInStackTrace();
             Slog.i(TAG, "Adding activity " + r + " to stack at " + addPos, here);
         }
+        r.task.addActivityToTop(r);
         mHistory.add(addPos, r);
         r.putInHistory();
         r.frontOfTask = newTask;
@@ -1986,6 +2115,9 @@
         if (doResume) {
             resumeTopActivityLocked(null);
         }
+        if (VALIDATE_TASK_REPLACE) {
+            verifyActivityRecords();
+        }
     }
 
     final void validateAppTokensLocked() {
@@ -2107,8 +2239,8 @@
                             if (mService.mCurTask <= 0) {
                                 mService.mCurTask = 1;
                             }
-                            target.setTask(new TaskRecord(mService.mCurTask, target.info, null),
-                                    null, false);
+                            target.setTask(createTaskRecord(mService.mCurTask, target.info, null,
+                                    false), null, false);
                             target.task.affinityIntent = target.intent;
                             if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
                                     + " out to new task " + target.task);
@@ -2172,8 +2304,7 @@
                             // like these are all in the reply chain.
                             replyChainEnd = targetI+1;
                             while (replyChainEnd < mHistory.size() &&
-                                    (mHistory.get(
-                                                replyChainEnd)).task == task) {
+                                    (mHistory.get(replyChainEnd)).task == task) {
                                 replyChainEnd++;
                             }
                             replyChainEnd--;
@@ -2346,6 +2477,9 @@
             }
         }
 
+        if (VALIDATE_TASK_REPLACE) {
+            verifyActivityRecords();
+        }
         return taskTop;
     }
     
@@ -3011,7 +3145,7 @@
                 if (mService.mCurTask <= 0) {
                     mService.mCurTask = 1;
                 }
-                r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
+                r.setTask(createTaskRecord(mService.mCurTask, r.info, intent, true), null, true);
                 if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                         + " in new task " + r.task);
             } else {
@@ -3075,7 +3209,7 @@
                 N > 0 ? mHistory.get(N-1) : null;
             r.setTask(prev != null
                     ? prev.task
-                    : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
+                    : createTaskRecord(mService.mCurTask, r.info, intent, true), null, true);
             if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                     + " in new guessed " + r.task);
         }
@@ -4160,6 +4294,9 @@
             here.fillInStackTrace();
             Slog.i(TAG, "Removing activity " + r + " from stack");
         }
+        if (r.task != null) {
+            r.task.removeActivity(r);
+        }
         mHistory.remove(r);
         r.takeFromHistory();
         removeTimeoutsForActivityLocked(r);
@@ -5169,4 +5306,185 @@
 
         return starting;
     }
+
+    void verifyActivityRecords() {
+        /* Until we have activity movement implemented for tasks just do the simple test
+        ActivityIterator iterator = new ActivityIterator();
+        int i;
+        int N = mHistory.size();
+        for (i = 0; i < N && iterator.hasNext(); ++i) {
+            ActivityRecord r1 = mHistory.get(i);
+            ActivityRecord r2 = iterator.next();
+            if (r1 != r2) {
+                break;
+            }
+        }
+        if (i != N || iterator.hasNext()) {
+            Slog.w(TAG, "verifyActivityRecords mHistory=" + mHistory
+                + " mTaskHistory=" + iterator + " Callers=" + Debug.getCallers(2));
+        } */
+        // Simple test
+        ActivityIterator iterator = new ActivityIterator();
+        while (iterator.hasNext()) {
+            ActivityRecord r = iterator.next();
+            if (!mHistory.contains(r)) {
+                break;
+            }
+        }
+        if (iterator.size() != mHistory.size() || iterator.hasNext()) {
+            Slog.w(TAG, "verifyActivityRecords mHistory=" + mHistory
+                + " mTaskHistory=" + iterator + " Callers=" + Debug.getCallers(2));
+        }
+    }
+
+    private TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
+            boolean toTop) {
+        TaskRecord oldTask = mTaskIdToTaskRecord.get(taskId);
+        if (oldTask != null) {
+            Slog.w(TAG, "createTaskRecord: Reusing taskId=" + taskId + " without removing");
+            mTaskHistory.remove(oldTask);
+        }
+        TaskRecord task = new TaskRecord(taskId, info, intent);
+        mTaskIdToTaskRecord.put(taskId, task);
+        if (toTop) {
+            mTaskHistory.add(task);
+        } else {
+            mTaskHistory.add(0, task);
+        }
+        return task;
+    }
+
+    class TaskIterator implements Iterator<TaskRecord> {
+        private int mCur;
+        private boolean mReverse;
+
+        TaskIterator() {
+            this(FORWARD_ITERATOR);
+        }
+
+        TaskIterator(boolean reverse) {
+            reset(reverse);
+        }
+
+        public void reset(boolean reverse) {
+            mReverse = reverse;
+            mCur = reverse ? mTaskHistory.size() - 1 : 0;
+        }
+
+        @Override
+        public boolean hasNext() {
+            if (mReverse) {
+                return mCur >= 0;
+            }
+            return mCur < mTaskHistory.size();
+        }
+
+        @Override
+        public TaskRecord next() {
+            if (hasNext()) {
+                TaskRecord task = mTaskHistory.get(mCur);
+                mCur += (mReverse ? -1 : 1);
+                return task;
+            }
+            throw new NoSuchElementException();
+        }
+
+        @Override
+        public void remove() {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    class ActivityIterator implements Iterator<ActivityRecord> {
+        final TaskIterator mIterator;
+        boolean mReverse;
+        int mCur;
+        TaskRecord mTaskRecord;
+        final boolean mSkipFinishing;
+
+        public ActivityIterator() {
+            this(FORWARD_ITERATOR);
+        }
+
+        public ActivityIterator(boolean reverse) {
+            this(reverse, false);
+        }
+
+        public ActivityIterator(boolean reverse, boolean skipFinishing) {
+            mSkipFinishing = skipFinishing;
+            mIterator = new TaskIterator();
+            reset(reverse);
+        }
+
+        public void reset(boolean reverse) {
+            mReverse = reverse;
+            mIterator.reset(reverse);
+            getNextTaskRecord();
+        }
+
+        private void getNextTaskRecord() {
+            if (mIterator.hasNext()) {
+                mTaskRecord = mIterator.next();
+                mCur = mReverse ? mTaskRecord.mActivities.size() - 1 : 0;
+            }
+        }
+
+        @Override
+        public boolean hasNext() {
+            if (mTaskRecord == null) {
+                return false;
+            }
+            if (mReverse) {
+                return mCur >= 0;
+            }
+            return mCur < mTaskRecord.mActivities.size();
+        }
+
+        @Override
+        public ActivityRecord next() {
+            while (hasNext()) {
+                ActivityRecord r = mTaskRecord.mActivities.get(mCur);
+                mCur += mReverse ? -1 : 1;
+                if (!hasNext()) {
+                    getNextTaskRecord();
+                }
+                if (mSkipFinishing && r.finishing) {
+                    continue;
+                }
+                return r;
+            }
+            throw new NoSuchElementException();
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        int size() {
+            int size = 0;
+            final TaskIterator iterator = new TaskIterator();
+            while (iterator.hasNext()) {
+                size += iterator.next().mActivities.size();
+            }
+            return size;
+        }
+
+        ActivityRecord peek() {
+            if (mTaskRecord != null && mCur >= 0 && mCur < mTaskRecord.mActivities.size()) {
+                return mTaskRecord.mActivities.get(mCur);
+            }
+            return null;
+        }
+
+        @Override
+        public String toString() {
+            StringBuffer sb = new StringBuffer();
+            for (int i = 0; i < mTaskHistory.size(); ++i) {
+                final TaskRecord task = mTaskHistory.get(i);
+                sb.append("task_").append(i).append("-").append(task.mActivities).append(" ");
+            }
+            return sb.toString();
+        }
+    }
 }
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index 1bae9ca..347aa7d 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -23,6 +23,7 @@
 import android.util.Slog;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 
 class TaskRecord extends ThumbnailHolder {
     final int taskId;       // Unique identifier for this task.
@@ -39,7 +40,11 @@
 
     String stringName;      // caching of toString() result.
     int userId;             // user for which this task was created
-    
+
+    int numFullscreen;      // Number of fullscreen activities.
+
+    final ArrayList<ActivityRecord> mActivities = new ArrayList<ActivityRecord>();
+
     TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
         taskId = _taskId;
         affinity = info.taskAffinity;
@@ -104,12 +109,49 @@
             userId = UserHandle.getUserId(info.applicationInfo.uid);
         }
     }
-    
+
+    ActivityRecord getTopActivity() {
+        for (int i = mActivities.size() - 1; i >= 0; --i) {
+            final ActivityRecord r = mActivities.get(i);
+            if (r.finishing) {
+                continue;
+            }
+            return r;
+        }
+        return null;
+    }
+
+    void addActivityAtBottom(ActivityRecord r) {
+        if (!mActivities.remove(r) && r.fullscreen) {
+            // Was not previously in list.
+            numFullscreen++;
+        }
+        mActivities.add(0, r);
+    }
+
+    void addActivityToTop(ActivityRecord r) {
+        if (!mActivities.remove(r) && r.fullscreen) {
+            // Was not previously in list.
+            numFullscreen++;
+        }
+        mActivities.add(r);
+    }
+
+    /** @return true if this was the last activity in the task */
+    boolean removeActivity(ActivityRecord r) {
+        if (mActivities.remove(r) && r.fullscreen) {
+            // Was previously in list.
+            numFullscreen--;
+        }
+        return mActivities.size() == 0;
+    }
+
     void dump(PrintWriter pw, String prefix) {
         if (numActivities != 0 || rootWasReset || userId != 0) {
             pw.print(prefix); pw.print("numActivities="); pw.print(numActivities);
                     pw.print(" rootWasReset="); pw.print(rootWasReset);
-                    pw.print(" userId="); pw.println(userId);
+                    pw.print(" userId="); pw.print(userId);
+                    pw.print(" numFullscreen="); pw.println(numFullscreen);
         }
         if (affinity != null) {
             pw.print(prefix); pw.print("affinity="); pw.println(affinity);
@@ -136,6 +178,7 @@
             pw.print(prefix); pw.print("realActivity=");
             pw.println(realActivity.flattenToShortString());
         }
+        pw.print(prefix); pw.print("Activities="); pw.println(mActivities);
         if (!askedCompatMode) {
             pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode);
         }
@@ -146,6 +189,7 @@
                 pw.print((getInactiveDuration()/1000)); pw.println("s)");
     }
 
+    @Override
     public String toString() {
         if (stringName != null) {
             return stringName;
@@ -156,19 +200,21 @@
         sb.append(" #");
         sb.append(taskId);
         if (affinity != null) {
-            sb.append(" A ");
+            sb.append(" A=");
             sb.append(affinity);
         } else if (intent != null) {
-            sb.append(" I ");
+            sb.append(" I=");
             sb.append(intent.getComponent().flattenToShortString());
         } else if (affinityIntent != null) {
-            sb.append(" aI ");
+            sb.append(" aI=");
             sb.append(affinityIntent.getComponent().flattenToShortString());
         } else {
             sb.append(" ??");
         }
-        sb.append(" U ");
+        sb.append(" U=");
         sb.append(userId);
+        sb.append(" sz=");
+        sb.append(mActivities.size());
         sb.append('}');
         return stringName = sb.toString();
     }
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index cc7c8b0..938fa5c 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -228,6 +228,10 @@
         public void remove() {
             throw new IllegalArgumentException();
         }
+
+        @Override public String toString() {
+            return mTaskLists.toString();
+        }
     }
 
     class AppTokenIterator implements Iterator<AppWindowToken> {
@@ -294,6 +298,10 @@
             }
             return size;
         }
+
+        @Override public String toString() {
+            return mIterator.toString();
+        }
     }
 
     public void dump(String prefix, PrintWriter pw) {
diff --git a/services/java/com/android/server/wm/TaskGroup.java b/services/java/com/android/server/wm/TaskGroup.java
index 5e82af2..1f1dd58 100644
--- a/services/java/com/android/server/wm/TaskGroup.java
+++ b/services/java/com/android/server/wm/TaskGroup.java
@@ -23,4 +23,9 @@
 public class TaskGroup {
     public int taskId = -1;
     public ArrayList<IApplicationToken> tokens = new ArrayList<IApplicationToken>();
+
+    @Override
+    public String toString() {
+        return "id=" + taskId + " tokens=" + tokens;
+    }
 }
diff --git a/services/java/com/android/server/wm/TaskList.java b/services/java/com/android/server/wm/TaskList.java
index 88791f2..67dfa4f 100644
--- a/services/java/com/android/server/wm/TaskList.java
+++ b/services/java/com/android/server/wm/TaskList.java
@@ -27,4 +27,9 @@
         mAppTokens.add(wtoken);
         mDisplayContent = displayContent;
     }
+
+    @Override
+    public String toString() {
+        return "id=" + taskId + " appTokens=" + mAppTokens;
+    }
 }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index e6d6436..b193430 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -3124,8 +3124,9 @@
                 return;
             }
 
+            boolean mismatch = false;
             AppTokenIterator iterator = displayContent.getTmpAppIterator(REVERSE_ITERATOR);
-            for ( ; t >= 0; --t) {
+            for ( ; t >= 0 && !mismatch; --t) {
                 task = tasks.get(t);
                 List<IApplicationToken> tokens = task.tokens;
                 int v = task.tokens.size() - 1;
@@ -3137,28 +3138,22 @@
                     return;
                 }
 
-                while (v >= 0 && iterator.hasNext()) {
+                while (v >= 0) {
                     AppWindowToken atoken = iterator.next();
                     if (atoken.removed) {
                         continue;
                     }
                     if (tokens.get(v) != atoken.token) {
-                        Slog.w(TAG, "Tokens out of sync: external is " + tokens.get(v)
-                              + " @ " + v + ", internal is " + atoken.token);
+                        mismatch = true;
+                        break;
                     }
                     v--;
                 }
-                while (v >= 0) {
-                    Slog.w(TAG, "External token not found: " + tokens.get(v) + " @ " + v);
-                    v--;
-                }
             }
 
-            while (iterator.hasNext()) {
-                AppWindowToken atoken = iterator.next();
-                if (!atoken.removed) {
-                    Slog.w(TAG, "Invalid internal atoken: " + atoken.token);
-                }
+            if (mismatch || iterator.hasNext()) {
+                Slog.w(TAG, "validateAppTokens: Mismatch! ActivityManager=" + tasks
+                        + " WindowManager=" + iterator);
             }
         }
     }