Merge "Switch DisplayListData to a staging model"
diff --git a/api/current.txt b/api/current.txt
index 3230bd8..d7664b9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10632,6 +10632,7 @@
     method public void setTileModeXY(android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
     method public final void setTileModeY(android.graphics.Shader.TileMode);
     method public void setTint(android.content.res.ColorStateList);
+    method public void setTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public class ClipDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
@@ -10909,6 +10910,7 @@
     method public void setTargetDensity(android.util.DisplayMetrics);
     method public void setTargetDensity(int);
     method public void setTint(android.content.res.ColorStateList);
+    method public void setTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public class PaintDrawable extends android.graphics.drawable.ShapeDrawable {
@@ -10999,6 +11001,9 @@
 
   public class TouchFeedbackDrawable extends android.graphics.drawable.LayerDrawable {
     method public android.graphics.Rect getDirtyBounds();
+    method public android.content.res.ColorStateList getTint();
+    method public void setTint(android.content.res.ColorStateList);
+    method public void setTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public class TransitionDrawable extends android.graphics.drawable.LayerDrawable implements android.graphics.drawable.Drawable.Callback {
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 0d4a4cb..c5e5753 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -461,6 +461,10 @@
             return (T) getFaces();
         } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
             return (T) getFaceRectangles();
+        } else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS)) {
+            return (T) getAvailableStreamConfigurations();
+        } else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS)) {
+            return (T) getAvailableMinFrameDurations();
         }
 
         // For other keys, get() falls back to getBase()
@@ -481,6 +485,50 @@
         return availableFormats;
     }
 
+    private int[] getAvailableStreamConfigurations() {
+        final int NUM_ELEMENTS_IN_CONFIG = 4;
+        int[] availableConfigs =
+                getBase(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+        if (availableConfigs != null) {
+            if (availableConfigs.length % NUM_ELEMENTS_IN_CONFIG != 0) {
+                Log.w(TAG, "availableStreamConfigurations is malformed, length must be multiple"
+                        + " of " + NUM_ELEMENTS_IN_CONFIG);
+                return availableConfigs;
+            }
+
+            for (int i = 0; i < availableConfigs.length; i += NUM_ELEMENTS_IN_CONFIG) {
+                // JPEG has different value between native and managed side, need override.
+                if (availableConfigs[i] == NATIVE_JPEG_FORMAT) {
+                    availableConfigs[i] = ImageFormat.JPEG;
+                }
+            }
+        }
+
+        return availableConfigs;
+    }
+
+    private long[] getAvailableMinFrameDurations() {
+        final int NUM_ELEMENTS_IN_DURATION = 4;
+        long[] availableMinDurations =
+                getBase(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
+        if (availableMinDurations != null) {
+            if (availableMinDurations.length % NUM_ELEMENTS_IN_DURATION != 0) {
+                Log.w(TAG, "availableStreamConfigurations is malformed, length must be multiple"
+                        + " of " + NUM_ELEMENTS_IN_DURATION);
+                return availableMinDurations;
+            }
+
+            for (int i = 0; i < availableMinDurations.length; i += NUM_ELEMENTS_IN_DURATION) {
+                // JPEG has different value between native and managed side, need override.
+                if (availableMinDurations[i] == NATIVE_JPEG_FORMAT) {
+                    availableMinDurations[i] = ImageFormat.JPEG;
+                }
+            }
+        }
+
+        return availableMinDurations;
+    }
+
     private Face[] getFaces() {
         final int FACE_LANDMARK_SIZE = 6;
 
@@ -607,12 +655,56 @@
             return setAvailableFormats((int[]) value);
         } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
             return setFaceRectangles((Rect[]) value);
+        } else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS)) {
+            return setAvailableStreamConfigurations((int[])value);
+        } else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS)) {
+            return setAvailableMinFrameDurations((long[])value);
         }
 
         // For other keys, set() falls back to setBase().
         return false;
     }
 
+    private boolean setAvailableStreamConfigurations(int[] value) {
+        final int NUM_ELEMENTS_IN_CONFIG = 4;
+        int[] availableConfigs = value;
+        if (value == null) {
+            // Let setBase() to handle the null value case.
+            return false;
+        }
+
+        int[] newValues = new int[availableConfigs.length];
+        for (int i = 0; i < availableConfigs.length; i++) {
+            newValues[i] = availableConfigs[i];
+            if (i % NUM_ELEMENTS_IN_CONFIG == 0 && availableConfigs[i] == ImageFormat.JPEG) {
+                newValues[i] = NATIVE_JPEG_FORMAT;
+            }
+        }
+
+        setBase(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS, newValues);
+        return true;
+    }
+
+    private boolean setAvailableMinFrameDurations(long[] value) {
+        final int NUM_ELEMENTS_IN_DURATION = 4;
+        long[] availableDurations = value;
+        if (value == null) {
+            // Let setBase() to handle the null value case.
+            return false;
+        }
+
+        long[] newValues = new long[availableDurations.length];
+        for (int i = 0; i < availableDurations.length; i++) {
+            newValues[i] = availableDurations[i];
+            if (i % NUM_ELEMENTS_IN_DURATION == 0 && availableDurations[i] == ImageFormat.JPEG) {
+                newValues[i] = NATIVE_JPEG_FORMAT;
+            }
+        }
+
+        setBase(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS, newValues);
+        return true;
+    }
+
     private boolean setAvailableFormats(int[] value) {
         int[] availableFormat = value;
         if (value == null) {
diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java
index 05f3a1c..1754dce 100644
--- a/core/java/android/provider/SearchIndexablesContract.java
+++ b/core/java/android/provider/SearchIndexablesContract.java
@@ -58,33 +58,64 @@
      * Indexable xml resources colums.
      */
     public static final String[] INDEXABLES_XML_RES_COLUMNS = new String[] {
-            XmlResource.COLUMN_RANK,
-            XmlResource.COLUMN_XML_RESID,
-            XmlResource.COLUMN_CLASS_NAME,
-            XmlResource.COLUMN_ICON_RESID,
-            XmlResource.COLUMN_INTENT_ACTION,
-            XmlResource.COLUMN_INTENT_TARGET_PACKAGE,
-            XmlResource.COLUMN_INTENT_TARGET_CLASS
+            XmlResource.COLUMN_RANK,                    // 0
+            XmlResource.COLUMN_XML_RESID,               // 1
+            XmlResource.COLUMN_CLASS_NAME,              // 2
+            XmlResource.COLUMN_ICON_RESID,              // 3
+            XmlResource.COLUMN_INTENT_ACTION,           // 4
+            XmlResource.COLUMN_INTENT_TARGET_PACKAGE,   // 5
+            XmlResource.COLUMN_INTENT_TARGET_CLASS      // 6
     };
 
     /**
+     * Indexable xml resources colums indices.
+     */
+    public static final int COLUMN_INDEX_XML_RES_RANK = 0;
+    public static final int COLUMN_INDEX_XML_RES_RESID = 1;
+    public static final int COLUMN_INDEX_XML_RES_CLASS_NAME = 2;
+    public static final int COLUMN_INDEX_XML_RES_ICON_RESID = 3;
+    public static final int COLUMN_INDEX_XML_RES_INTENT_ACTION = 4;
+    public static final int COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE = 5;
+    public static final int COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS = 6;
+
+    /**
      * Indexable raw data colums.
      */
     public static final String[] INDEXABLES_RAW_COLUMNS = new String[] {
-            RawData.COLUMN_RANK,
-            RawData.COLUMN_TITLE,
-            RawData.COLUMN_SUMMARY_ON,
-            RawData.COLUMN_SUMMARY_OFF,
-            RawData.COLUMN_KEYWORDS,
-            RawData.COLUMN_SCREEN_TITLE,
-            RawData.COLUMN_CLASS_NAME,
-            RawData.COLUMN_ICON_RESID,
-            RawData.COLUMN_INTENT_ACTION,
-            RawData.COLUMN_INTENT_TARGET_PACKAGE,
-            RawData.COLUMN_INTENT_TARGET_CLASS,
+            RawData.COLUMN_RANK,                    // 0
+            RawData.COLUMN_TITLE,                   // 1
+            RawData.COLUMN_SUMMARY_ON,              // 2
+            RawData.COLUMN_SUMMARY_OFF,             // 3
+            RawData.COLUMN_ENTRIES,                 // 4
+            RawData.COLUMN_KEYWORDS,                // 5
+            RawData.COLUMN_SCREEN_TITLE,            // 6
+            RawData.COLUMN_CLASS_NAME,              // 7
+            RawData.COLUMN_ICON_RESID,              // 8
+            RawData.COLUMN_INTENT_ACTION,           // 9
+            RawData.COLUMN_INTENT_TARGET_PACKAGE,   // 10
+            RawData.COLUMN_INTENT_TARGET_CLASS,     // 11
+            RawData.COLUMN_KEY,                     // 12
     };
 
     /**
+     * Indexable raw data colums indices.
+     */
+    public static final int COLUMN_INDEX_RAW_RANK = 0;
+    public static final int COLUMN_INDEX_RAW_TITLE = 1;
+    public static final int COLUMN_INDEX_RAW_SUMMARY_ON = 2;
+    public static final int COLUMN_INDEX_RAW_SUMMARY_OFF = 3;
+    public static final int COLUMN_INDEX_RAW_ENTRIES = 4;
+    public static final int COLUMN_INDEX_RAW_KEYWORDS = 5;
+    public static final int COLUMN_INDEX_RAW_SCREEN_TITLE = 6;
+    public static final int COLUMN_INDEX_RAW_CLASS_NAME = 7;
+    public static final int COLUMN_INDEX_RAW_ICON_RESID = 8;
+    public static final int COLUMN_INDEX_RAW_INTENT_ACTION = 9;
+    public static final int COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE = 10;
+    public static final int COLUMN_INDEX_RAW_INTENT_TARGET_CLASS = 11;
+    public static final int COLUMN_INDEX_RAW_KEY = 12;
+
+
+    /**
      * Constants related to a {@link SearchIndexableResource}.
      *
      * This is a description of
@@ -134,6 +165,11 @@
         public static final String COLUMN_SUMMARY_OFF = "summaryOff";
 
         /**
+         * Entries associated with the raw data (when the data can can several values).
+         */
+        public static final String COLUMN_ENTRIES = "entries";
+
+        /**
          * Keywords' raw data.
          */
         public static final String COLUMN_KEYWORDS = "keywords";
@@ -142,6 +178,11 @@
          * Fragment's title associated with the raw data.
          */
         public static final String COLUMN_SCREEN_TITLE = "screenTitle";
+
+        /**
+         * Key associated with the raw data. The key needs to be unique.
+         */
+        public static final String COLUMN_KEY = "key";
     }
 
     /**
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
index f90a763..b589cc2 100644
--- a/core/java/android/view/GLRenderer.java
+++ b/core/java/android/view/GLRenderer.java
@@ -838,6 +838,11 @@
         }
     }
 
+    @Override
+    void pauseSurface(Surface surface) {
+        // No-op
+    }
+
     boolean initializeEgl() {
         synchronized (sEglLock) {
             if (sEgl == null && sEglConfig == null) {
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index ea707b0..56d96e1 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -234,6 +234,13 @@
     abstract void updateSurface(Surface surface) throws OutOfResourcesException;
 
     /**
+     * Stops any rendering into the surface. Use this if it is unclear whether
+     * or not the surface used by the HardwareRenderer will be changing. It
+     * Suspends any rendering into the surface, but will not do any destruction
+     */
+    abstract void pauseSurface(Surface surface);
+
+    /**
      * Destroys all hardware rendering resources associated with the specified
      * view hierarchy.
      *
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 6ae730b..1ecc3c6 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -54,28 +54,46 @@
 
     private int mWidth, mHeight;
     private long mNativeProxy;
+    private boolean mInitialized = false;
 
     ThreadedRenderer(boolean translucent) {
         mNativeProxy = nCreateProxy(translucent);
-        setEnabled(mNativeProxy != 0);
     }
 
     @Override
     void destroy(boolean full) {
+        mInitialized = false;
+        updateEnabledState(null);
         nDestroyCanvas(mNativeProxy);
     }
 
+    private void updateEnabledState(Surface surface) {
+        if (surface == null || !surface.isValid()) {
+            setEnabled(false);
+        } else {
+            setEnabled(mInitialized);
+        }
+    }
+
     @Override
     boolean initialize(Surface surface) throws OutOfResourcesException {
+        mInitialized = true;
+        updateEnabledState(surface);
         return nInitialize(mNativeProxy, surface);
     }
 
     @Override
     void updateSurface(Surface surface) throws OutOfResourcesException {
+        updateEnabledState(surface);
         nUpdateSurface(mNativeProxy, surface);
     }
 
     @Override
+    void pauseSurface(Surface surface) {
+        nPauseSurface(mNativeProxy, surface);
+    }
+
+    @Override
     void destroyHardwareResources(View view) {
         destroyResources(view);
         // TODO: GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
@@ -262,6 +280,7 @@
 
     private static native boolean nInitialize(long nativeProxy, Surface window);
     private static native void nUpdateSurface(long nativeProxy, Surface window);
+    private static native void nPauseSurface(long nativeProxy, Surface window);
     private static native void nSetup(long nativeProxy, int width, int height);
     private static native void nSetDisplayListData(long nativeProxy, long displayList,
             long newData);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0010eeb..65ac50d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1421,6 +1421,12 @@
                             host.getMeasuredHeight() + ", params=" + params);
                 }
 
+                if (mAttachInfo.mHardwareRenderer != null) {
+                    // relayoutWindow may decide to destroy mSurface. As that decision
+                    // happens in WindowManager service, we need to be defensive here
+                    // and stop using the surface in case it gets destroyed.
+                    mAttachInfo.mHardwareRenderer.pauseSurface(mSurface);
+                }
                 final int surfaceGenerationId = mSurface.getGenerationId();
                 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
                 if (!mDrawDuringWindowsAnimating &&
diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp
index 2593420..8a0eaa2 100644
--- a/core/jni/android_util_EventLog.cpp
+++ b/core/jni/android_util_EventLog.cpp
@@ -177,13 +177,13 @@
             break;
         }
         if (ret < 0) {
-            if (errno == EINTR) {
+            if (ret == -EINTR) {
                 continue;
             }
-            if (errno == EINVAL) {
+            if (ret == -EINVAL) {
                 jniThrowException(env, "java/io/IOException", "Event too short");
-            } else if (errno != EAGAIN) {
-                jniThrowIOException(env, errno);  // Will throw on return
+            } else if (ret != -EAGAIN) {
+                jniThrowIOException(env, -ret);  // Will throw on return
             }
             break;
         }
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 42cbfc9..36c8357 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -84,7 +84,7 @@
         jlong proxyPtr, jobject jsurface) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
     sp<ANativeWindow> window = android_view_Surface_getNativeWindow(env, jsurface);
-    return proxy->initialize(window.get());
+    return proxy->initialize(window);
 }
 
 static void android_view_ThreadedRenderer_updateSurface(JNIEnv* env, jobject clazz,
@@ -94,7 +94,17 @@
     if (jsurface) {
         window = android_view_Surface_getNativeWindow(env, jsurface);
     }
-    proxy->updateSurface(window.get());
+    proxy->updateSurface(window);
+}
+
+static void android_view_ThreadedRenderer_pauseSurface(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jobject jsurface) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    sp<ANativeWindow> window;
+    if (jsurface) {
+        window = android_view_Surface_getNativeWindow(env, jsurface);
+    }
+    proxy->pauseSurface(window);
 }
 
 static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz,
@@ -195,6 +205,7 @@
     { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
     { "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize },
     { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
+    { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
     { "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup },
     { "nDrawDisplayList", "(JJIIII)V", (void*) android_view_ThreadedRenderer_drawDisplayList },
     { "nDestroyCanvas", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvas },
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 46cb9b2..e100a4d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4409,14 +4409,23 @@
         <!-- When a tint color is set, specifies its Porter-Duff blending mode. The
              default value is src_in, which treats the drawable as an alpha mask. -->
         <attr name="tintMode">
-            <!-- [Sa * Da, Sc * Da] -->
-            <enum name="src_in" value="0" />
-            <!-- [Da, Sc * Da + (1 - Sa) * Dc] -->
-            <enum name="src_atop" value="1" />
-            <!-- [Sa * Da, Sc * Dc] -->
-            <enum name="multiply" value="2" />
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
             <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
-            <enum name="screen" value="3" />
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and drawable color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
         </attr>
     </declare-styleable>
 
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 7aea814..37a903a 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -349,6 +349,7 @@
         <item name="actionMenuTextAppearance">@android:style/TextAppearance.Holo.Widget.ActionBar.Menu</item>
         <item name="actionMenuTextColor">?android:attr/textColorPrimary</item>
         <item name="actionBarWidgetTheme">@null</item>
+        <item name="actionBarTheme">@null</item>
         <item name="actionBarDivider">?android:attr/dividerVertical</item>
         <item name="actionBarItemBackground">?android:attr/selectableItemBackground</item>
 
@@ -1194,6 +1195,7 @@
         <item name="actionBarSize">@dimen/action_bar_default_height</item>
         <item name="actionModePopupWindowStyle">@android:style/Widget.Holo.PopupWindow.ActionMode</item>
         <item name="actionBarWidgetTheme">@null</item>
+        <item name="actionBarTheme">@null</item>
 
         <item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_holo_dark</item>
         <item name="actionModeCopyDrawable">@android:drawable/ic_menu_copy_holo_dark</item>
@@ -1526,6 +1528,7 @@
         <item name="actionBarSize">@dimen/action_bar_default_height</item>
         <item name="actionModePopupWindowStyle">@android:style/Widget.Holo.Light.PopupWindow.ActionMode</item>
         <item name="actionBarWidgetTheme">@null</item>
+        <item name="actionBarTheme">@null</item>
 
         <item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_holo_light</item>
         <item name="actionModeCopyDrawable">@android:drawable/ic_menu_copy_holo_light</item>
@@ -1585,6 +1588,7 @@
         <item name="android:windowContentOverlay">@android:drawable/ab_solid_shadow_holo</item>
         <item name="android:actionBarStyle">@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse</item>
         <item name="actionBarWidgetTheme">@android:style/Theme.Holo</item>
+        <item name="actionBarTheme">@null</item>
 
         <item name="actionDropDownStyle">@android:style/Widget.Holo.Spinner.DropDown.ActionBar</item>
         <item name="actionButtonStyle">@android:style/Widget.Holo.ActionButton</item>
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
index b5ac8fa..2a80c81 100644
--- a/core/res/res/values/themes_quantum.xml
+++ b/core/res/res/values/themes_quantum.xml
@@ -706,6 +706,7 @@
          with an inverse color profile. The dark action bar sharply stands out against
          the light content. -->
     <style name="Theme.Quantum.Light.DarkActionBar">
+        <item name="actionBarWidgetTheme">@null</item>
         <item name="actionBarTheme">@style/Theme.Quantum</item>
     </style>
 
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 19131f2..66a88a2 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -595,7 +595,6 @@
      * Specifies the blending mode used to apply tint.
      *
      * @param tintMode A Porter-Duff blending mode
-     * @hide Pending finalization of supported Modes
      */
     public void setTintMode(Mode tintMode) {
         if (mBitmapState.mTintMode != tintMode) {
@@ -606,10 +605,7 @@
     }
 
     /**
-     * Returns the tint mode for this drawable, or {@code null} if none set.
-     *
-     * @return the tint mode for this drawable, or {@code null} if none set
-     * @hide
+     * @hide only needed by a hack within ProgressBar
      */
     public Mode getTintMode() {
         return mBitmapState.mTintMode;
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 077db7a..21cd5db 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1248,16 +1248,14 @@
      */
     static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) {
         switch (value) {
-            case 0:
-                return Mode.SRC_IN;
-            case 1:
-                return Mode.SRC_ATOP;
-            case 2:
-                return Mode.MULTIPLY;
-            case 3:
-                return Mode.SCREEN;
+            case 3: return Mode.SRC_OVER;
+            case 5: return Mode.SRC_IN;
+            case 9: return Mode.SRC_ATOP;
+            case 14: return Mode.MULTIPLY;
+            case 15: return Mode.SCREEN;
+            case 16: return Mode.ADD;
+            default: return defaultMode;
         }
-        return defaultMode;
     }
 }
 
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 66193a5..3e9ca0a 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -345,7 +345,6 @@
      * Specifies the blending mode used to apply tint.
      *
      * @param tintMode A Porter-Duff blending mode
-     * @hide Pending finalization of supported Modes
      */
     public void setTintMode(Mode tintMode) {
         if (mNinePatchState.mTintMode != tintMode) {
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
index 3323a25..2810c43 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
@@ -124,6 +124,41 @@
         return super.isStateful() || mState.mTint != null && mState.mTint.isStateful();
     }
 
+    /**
+     * Specifies a tint for drawing touch feedback ripples.
+     *
+     * @param tint Color state list to use for tinting touch feedback ripples,
+     *        or null to clear the tint
+     */
+    public void setTint(ColorStateList tint) {
+        if (mState.mTint != tint) {
+            mState.mTint = tint;
+            invalidateSelf();
+        }
+    }
+
+    /**
+     * Returns the tint color for touch feedback ripples.
+     *
+     * @return Color state list to use for tinting touch feedback ripples, or
+     *         null if none set
+     */
+    public ColorStateList getTint() {
+        return mState.mTint;
+    }
+
+    /**
+     * Specifies the blending mode used to draw touch feedback ripples.
+     *
+     * @param tintMode A Porter-Duff blending mode
+     */
+    public void setTintMode(Mode tintMode) {
+        if (mState.mTintMode != tintMode) {
+            mState.mTintMode = tintMode;
+            invalidateSelf();
+        }
+    }
+
     @Override
     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
             throws XmlPullParserException, IOException {
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index a84aa6b..140a07a 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -192,7 +192,9 @@
             flags, *currentTransform());
     addDrawOp(op);
     mDisplayListData->addChild(op);
-    if (displayList->isProjectionReceiver()) {
+
+    if (displayList->stagingProperties().isProjectionReceiver()) {
+        // use staging property, since recording on UI thread
         mDisplayListData->projectionReceiveIndex = mDisplayListData->displayListOps.size() - 1;
     }
 
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index fcf3b74..e5b9d7c 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -105,6 +105,10 @@
         return mDisplayListData && mDisplayListData->hasDrawOps;
     }
 
+    const char* getName() const {
+        return mName.string();
+    }
+
     void setName(const char* name) {
         if (name) {
             char* lastPeriod = strrchr(name, '.');
@@ -129,10 +133,6 @@
         return mStagingProperties;
     }
 
-    bool isProjectionReceiver() {
-        return properties().isProjectionReceiver();
-    }
-
     int getWidth() {
         return properties().getWidth();
     }
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ad2e330..014c7d0 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -347,6 +347,7 @@
 
     if (mEglSurface != EGL_NO_SURFACE) {
         mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface);
+        mGlobalContext->makeCurrent(mEglSurface);
         mHaveNewSurface = true;
     }
 }
@@ -356,14 +357,15 @@
     mHaveNewSurface = false;
 }
 
-void CanvasContext::makeCurrent() {
+void CanvasContext::requireSurface() {
+    LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+            "requireSurface() called but no surface set!");
     mGlobalContext->makeCurrent(mEglSurface);
 }
 
 bool CanvasContext::initialize(EGLNativeWindowType window) {
     if (mCanvas) return false;
     setSurface(window);
-    makeCurrent();
     mCanvas = new OpenGLRenderer();
     mCanvas->initProperties();
     return true;
@@ -371,7 +373,11 @@
 
 void CanvasContext::updateSurface(EGLNativeWindowType window) {
     setSurface(window);
-    makeCurrent();
+}
+
+void CanvasContext::pauseSurface(EGLNativeWindowType window) {
+    // TODO: For now we just need a fence, in the future suspend any animations
+    // and such to prevent from trying to render into this surface
 }
 
 void CanvasContext::setup(int width, int height) {
@@ -456,7 +462,7 @@
 
     if (!mCanvas) return;
 
-    makeCurrent();
+    requireSurface();
     Rect dirty;
     mCanvas->invokeFunctors(dirty);
 }
@@ -487,12 +493,12 @@
 }
 
 Layer* CanvasContext::createRenderLayer(int width, int height) {
-    requireGlContext();
+    requireSurface();
     return LayerRenderer::createRenderLayer(width, height);
 }
 
 Layer* CanvasContext::createTextureLayer() {
-    requireGlContext();
+    requireSurface();
     return LayerRenderer::createTextureLayer();
 }
 
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index c878e8f..4d830ba 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -63,6 +63,7 @@
 
     bool initialize(EGLNativeWindowType window);
     void updateSurface(EGLNativeWindowType window);
+    void pauseSurface(EGLNativeWindowType window);
     void setup(int width, int height);
     void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters);
     void drawDisplayList(RenderNode* displayList, Rect* dirty);
@@ -82,7 +83,7 @@
 private:
     void setSurface(EGLNativeWindowType window);
     void swapBuffers();
-    void makeCurrent();
+    void requireSurface();
 
     friend class InvokeFunctorsTask;
     void invokeFunctors();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index d238409..49b9aca 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -92,10 +92,10 @@
     return (void*) args->context->initialize(args->window);
 }
 
-bool RenderProxy::initialize(EGLNativeWindowType window) {
+bool RenderProxy::initialize(const sp<ANativeWindow>& window) {
     SETUP_TASK(initialize);
     args->context = mContext;
-    args->window = window;
+    args->window = window.get();
     return (bool) postAndWait(task);
 }
 
@@ -104,11 +104,23 @@
     return NULL;
 }
 
-void RenderProxy::updateSurface(EGLNativeWindowType window) {
+void RenderProxy::updateSurface(const sp<ANativeWindow>& window) {
     SETUP_TASK(updateSurface);
     args->context = mContext;
-    args->window = window;
-    post(task);
+    args->window = window.get();
+    postAndWait(task);
+}
+
+CREATE_BRIDGE2(pauseSurface, CanvasContext* context, EGLNativeWindowType window) {
+    args->context->pauseSurface(args->window);
+    return NULL;
+}
+
+void RenderProxy::pauseSurface(const sp<ANativeWindow>& window) {
+    SETUP_TASK(pauseSurface);
+    args->context = mContext;
+    args->window = window.get();
+    postAndWait(task);
 }
 
 CREATE_BRIDGE3(setup, CanvasContext* context, int width, int height) {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 8cb414d..1ad3c85 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -59,8 +59,9 @@
     ANDROID_API RenderProxy(bool translucent);
     ANDROID_API virtual ~RenderProxy();
 
-    ANDROID_API bool initialize(EGLNativeWindowType window);
-    ANDROID_API void updateSurface(EGLNativeWindowType window);
+    ANDROID_API bool initialize(const sp<ANativeWindow>& window);
+    ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
+    ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
     ANDROID_API void setup(int width, int height);
     ANDROID_API void drawDisplayList(RenderNode* displayList,
             int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
index f688d81..664c707 100644
--- a/media/java/android/media/MediaFocusControl.java
+++ b/media/java/android/media/MediaFocusControl.java
@@ -471,10 +471,6 @@
                 final FocusRequester exFocusOwner = mFocusStack.pop();
                 exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS);
                 exFocusOwner.release();
-                // clear RCD
-                synchronized(mPRStack) {
-                    clearRemoteControlDisplay_syncAfRcs();
-                }
             }
         }
     }
@@ -535,10 +531,6 @@
             if (signal) {
                 // notify the new top of the stack it gained focus
                 notifyTopOfAudioFocusStack();
-                // there's a new top of the stack, let the remote control know
-                synchronized(mPRStack) {
-                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-                }
             }
         } else {
             // focus is abandoned by a client that's not at the top of the stack,
@@ -582,10 +574,6 @@
             // we removed an entry at the top of the stack:
             //  notify the new top of the stack it gained focus.
             notifyTopOfAudioFocusStack();
-            // there's a new top of the stack, let the remote control know
-            synchronized(mPRStack) {
-                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-            }
         }
     }
 
@@ -694,10 +682,6 @@
             mFocusStack.push(new FocusRequester(mainStreamType, focusChangeHint, fd, cb,
                     clientId, afdh, callingPackageName, Binder.getCallingUid()));
 
-            // there's a new top of the stack, let the remote control know
-            synchronized(mPRStack) {
-                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-            }
         }//synchronized(mAudioFocusLock)
 
         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
@@ -1237,11 +1221,11 @@
     /**
      * Helper function:
      * Set the new remote control receiver at the top of the RC focus stack.
-     * Called synchronized on mAudioFocusLock, then mPRStack
+     * Called synchronized on mPRStack
      * precondition: mediaIntent != null
      * @return true if mPRStack was changed, false otherwise
      */
-    private boolean pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent,
+    private boolean pushMediaButtonReceiver_syncPrs(PendingIntent mediaIntent,
             ComponentName target, IBinder token) {
         // already at top of stack?
         if (!mPRStack.empty() && mPRStack.peek().hasMatchingMediaButtonIntent(mediaIntent)) {
@@ -1285,10 +1269,10 @@
     /**
      * Helper function:
      * Remove the remote control receiver from the RC focus stack.
-     * Called synchronized on mAudioFocusLock, then mPRStack
+     * Called synchronized on mPRStack
      * precondition: pi != null
      */
-    private void removeMediaButtonReceiver_syncAfRcs(PendingIntent pi) {
+    private void removeMediaButtonReceiver_syncPrs(PendingIntent pi) {
         try {
             for (int index = mPRStack.size()-1; index >= 0; index--) {
                 final PlayerRecord prse = mPRStack.elementAt(index);
@@ -1470,7 +1454,7 @@
      * Helper function:
      * Called synchronized on mPRStack
      */
-    private void clearRemoteControlDisplay_syncAfRcs() {
+    private void clearRemoteControlDisplay_syncPrs() {
         synchronized(mCurrentRcLock) {
             mCurrentRcClient = null;
         }
@@ -1480,20 +1464,20 @@
 
     /**
      * Helper function for code readability: only to be called from
-     *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
+     *    checkUpdateRemoteControlDisplay_syncPrs() which checks the preconditions for
      *    this method.
      * Preconditions:
-     *    - called synchronized mAudioFocusLock then on mPRStack
+     *    - called synchronized on mPRStack
      *    - mPRStack.isEmpty() is false
      */
-    private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
+    private void updateRemoteControlDisplay_syncPrs(int infoChangedFlags) {
         PlayerRecord prse = mPRStack.peek();
         int infoFlagsAboutToBeUsed = infoChangedFlags;
         // this is where we enforce opt-in for information display on the remote controls
         //   with the new AudioManager.registerRemoteControlClient() API
         if (prse.getRcc() == null) {
             //Log.w(TAG, "Can't update remote control display with null remote control client");
-            clearRemoteControlDisplay_syncAfRcs();
+            clearRemoteControlDisplay_syncPrs();
             return;
         }
         synchronized(mCurrentRcLock) {
@@ -1511,62 +1495,25 @@
 
     /**
      * Helper function:
-     * Called synchronized on mAudioFocusLock, then mPRStack
+     * Called synchronized on mPRStack
      * Check whether the remote control display should be updated, triggers the update if required
      * @param infoChangedFlags the flags corresponding to the remote control client information
      *     that has changed, if applicable (checking for the update conditions might trigger a
      *     clear, rather than an update event).
      */
-    private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
+    private void checkUpdateRemoteControlDisplay_syncPrs(int infoChangedFlags) {
         // determine whether the remote control display should be refreshed
-        // if either stack is empty, there is a mismatch, so clear the RC display
-        if (mPRStack.isEmpty() || mFocusStack.isEmpty()) {
-            clearRemoteControlDisplay_syncAfRcs();
+        // if the player record stack is empty, there is nothing to display, so clear the RC display
+        if (mPRStack.isEmpty()) {
+            clearRemoteControlDisplay_syncPrs();
             return;
         }
 
-        // determine which entry in the AudioFocus stack to consider, and compare against the
-        // top of the stack for the media button event receivers : simply using the top of the
-        // stack would make the entry disappear from the RemoteControlDisplay in conditions such as
-        // notifications playing during music playback.
-        // Crawl the AudioFocus stack from the top until an entry is found with the following
-        // characteristics:
-        // - focus gain on STREAM_MUSIC stream
-        // - non-transient focus gain on a stream other than music
-        FocusRequester af = null;
-        try {
-            for (int index = mFocusStack.size()-1; index >= 0; index--) {
-                FocusRequester fr = mFocusStack.elementAt(index);
-                if ((fr.getStreamType() == AudioManager.STREAM_MUSIC)
-                        || (fr.getGainRequest() == AudioManager.AUDIOFOCUS_GAIN)) {
-                    af = fr;
-                    break;
-                }
-            }
-        } catch (ArrayIndexOutOfBoundsException e) {
-            Log.e(TAG, "Wrong index accessing audio focus stack when updating RCD: " + e);
-            af = null;
-        }
-        if (af == null) {
-            clearRemoteControlDisplay_syncAfRcs();
-            return;
-        }
-
-        // if the audio focus and RC owners belong to different packages, there is a mismatch, clear
-        if (!af.hasSamePackage(mPRStack.peek().getCallingPackageName())) {
-            clearRemoteControlDisplay_syncAfRcs();
-            return;
-        }
-        // if the audio focus didn't originate from the same Uid as the one in which the remote
-        //   control information will be retrieved, clear
-        if (!af.hasSameUid(mPRStack.peek().getCallingUid())) {
-            clearRemoteControlDisplay_syncAfRcs();
-            return;
-        }
+        // this is where more rules for refresh go
 
         // refresh conditions were verified: update the remote controls
-        // ok to call: synchronized mAudioFocusLock then on mPRStack, mPRStack is not empty
-        updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
+        // ok to call: synchronized on mPRStack, mPRStack is not empty
+        updateRemoteControlDisplay_syncPrs(infoChangedFlags);
     }
 
     /**
@@ -1582,35 +1529,33 @@
 
     private void onPromoteRcc(int rccId) {
         if (DEBUG_RC) { Log.d(TAG, "Promoting RCC " + rccId); }
-        synchronized(mAudioFocusLock) {
-            synchronized(mPRStack) {
-                // ignore if given RCC ID is already at top of remote control stack
-                if (!mPRStack.isEmpty() && (mPRStack.peek().getRccId() == rccId)) {
-                    return;
-                }
-                int indexToPromote = -1;
-                try {
-                    for (int index = mPRStack.size()-1; index >= 0; index--) {
-                        final PlayerRecord prse = mPRStack.elementAt(index);
-                        if (prse.getRccId() == rccId) {
-                            indexToPromote = index;
-                            break;
-                        }
+        synchronized(mPRStack) {
+            // ignore if given RCC ID is already at top of remote control stack
+            if (!mPRStack.isEmpty() && (mPRStack.peek().getRccId() == rccId)) {
+                return;
+            }
+            int indexToPromote = -1;
+            try {
+                for (int index = mPRStack.size()-1; index >= 0; index--) {
+                    final PlayerRecord prse = mPRStack.elementAt(index);
+                    if (prse.getRccId() == rccId) {
+                        indexToPromote = index;
+                        break;
                     }
-                    if (indexToPromote >= 0) {
-                        if (DEBUG_RC) { Log.d(TAG, "  moving RCC from index " + indexToPromote
-                                + " to " + (mPRStack.size()-1)); }
-                        final PlayerRecord prse = mPRStack.remove(indexToPromote);
-                        mPRStack.push(prse);
-                        // the RC stack changed, reevaluate the display
-                        checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-                    }
-                } catch (ArrayIndexOutOfBoundsException e) {
-                    // not expected to happen, indicates improper concurrent modification
-                    Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
                 }
-            }//synchronized(mPRStack)
-        }//synchronized(mAudioFocusLock)
+                if (indexToPromote >= 0) {
+                    if (DEBUG_RC) { Log.d(TAG, "  moving RCC from index " + indexToPromote
+                            + " to " + (mPRStack.size()-1)); }
+                    final PlayerRecord prse = mPRStack.remove(indexToPromote);
+                    mPRStack.push(prse);
+                    // the RC stack changed, reevaluate the display
+                    checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
+                }
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // not expected to happen, indicates improper concurrent modification
+                Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
+            }
+        }//synchronized(mPRStack)
     }
 
     /**
@@ -1621,12 +1566,10 @@
             IBinder token) {
         Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
 
-        synchronized(mAudioFocusLock) {
-            synchronized(mPRStack) {
-                if (pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver, token)) {
-                    // new RC client, assume every type of information shall be queried
-                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-                }
+        synchronized(mPRStack) {
+            if (pushMediaButtonReceiver_syncPrs(mediaIntent, eventReceiver, token)) {
+                // new RC client, assume every type of information shall be queried
+                checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
             }
         }
     }
@@ -1639,14 +1582,12 @@
     {
         Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
 
-        synchronized(mAudioFocusLock) {
-            synchronized(mPRStack) {
-                boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
-                removeMediaButtonReceiver_syncAfRcs(mediaIntent);
-                if (topOfStackWillChange) {
-                    // current RC client will change, assume every type of info needs to be queried
-                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-                }
+        synchronized(mPRStack) {
+            boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
+            removeMediaButtonReceiver_syncPrs(mediaIntent);
+            if (topOfStackWillChange) {
+                // current RC client will change, assume every type of info needs to be queried
+                checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
             }
         }
     }
@@ -1697,42 +1638,40 @@
             IRemoteControlClient rcClient, String callingPackageName) {
         if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
         int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
-        synchronized(mAudioFocusLock) {
-            synchronized(mPRStack) {
-                // store the new display information
-                try {
-                    for (int index = mPRStack.size()-1; index >= 0; index--) {
-                        final PlayerRecord prse = mPRStack.elementAt(index);
-                        if(prse.hasMatchingMediaButtonIntent(mediaIntent)) {
-                            prse.resetControllerInfoForRcc(rcClient, callingPackageName,
-                                    Binder.getCallingUid());
+        synchronized(mPRStack) {
+            // store the new display information
+            try {
+                for (int index = mPRStack.size()-1; index >= 0; index--) {
+                    final PlayerRecord prse = mPRStack.elementAt(index);
+                    if(prse.hasMatchingMediaButtonIntent(mediaIntent)) {
+                        prse.resetControllerInfoForRcc(rcClient, callingPackageName,
+                                Binder.getCallingUid());
 
-                            if (rcClient == null) {
-                                break;
-                            }
-
-                            rccId = prse.getRccId();
-
-                            // there is a new (non-null) client:
-                            //     give the new client the displays (if any)
-                            if (mRcDisplays.size() > 0) {
-                                plugRemoteControlDisplaysIntoClient_syncRcStack(prse.getRcc());
-                            }
+                        if (rcClient == null) {
                             break;
                         }
-                    }//for
-                } catch (ArrayIndexOutOfBoundsException e) {
-                    // not expected to happen, indicates improper concurrent modification
-                    Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
-                }
 
-                // if the eventReceiver is at the top of the stack
-                // then check for potential refresh of the remote controls
-                if (isCurrentRcController(mediaIntent)) {
-                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-                }
-            }//synchronized(mPRStack)
-        }//synchronized(mAudioFocusLock)
+                        rccId = prse.getRccId();
+
+                        // there is a new (non-null) client:
+                        //     give the new client the displays (if any)
+                        if (mRcDisplays.size() > 0) {
+                            plugRemoteControlDisplaysIntoClient_syncPrs(prse.getRcc());
+                        }
+                        break;
+                    }
+                }//for
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // not expected to happen, indicates improper concurrent modification
+                Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
+            }
+
+            // if the eventReceiver is at the top of the stack
+            // then check for potential refresh of the remote controls
+            if (isCurrentRcController(mediaIntent)) {
+                checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
+            }
+        }//synchronized(mPRStack)
         return rccId;
     }
 
@@ -1743,29 +1682,27 @@
     protected void unregisterRemoteControlClient(PendingIntent mediaIntent,
             IRemoteControlClient rcClient) {
         if (DEBUG_RC) Log.i(TAG, "Unregister remote control client rcClient="+rcClient);
-        synchronized(mAudioFocusLock) {
-            synchronized(mPRStack) {
-                boolean topRccChange = false;
-                try {
-                    for (int index = mPRStack.size()-1; index >= 0; index--) {
-                        final PlayerRecord prse = mPRStack.elementAt(index);
-                        if ((prse.hasMatchingMediaButtonIntent(mediaIntent))
-                                && rcClient.equals(prse.getRcc())) {
-                            // we found the IRemoteControlClient to unregister
-                            prse.resetControllerInfoForNoRcc();
-                            topRccChange = (index == mPRStack.size()-1);
-                            // there can only be one matching RCC in the RC stack, we're done
-                            break;
-                        }
+        synchronized(mPRStack) {
+            boolean topRccChange = false;
+            try {
+                for (int index = mPRStack.size()-1; index >= 0; index--) {
+                    final PlayerRecord prse = mPRStack.elementAt(index);
+                    if ((prse.hasMatchingMediaButtonIntent(mediaIntent))
+                            && rcClient.equals(prse.getRcc())) {
+                        // we found the IRemoteControlClient to unregister
+                        prse.resetControllerInfoForNoRcc();
+                        topRccChange = (index == mPRStack.size()-1);
+                        // there can only be one matching RCC in the RC stack, we're done
+                        break;
                     }
-                } catch (ArrayIndexOutOfBoundsException e) {
-                    // not expected to happen, indicates improper concurrent modification
-                    Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
                 }
-                if (topRccChange) {
-                    // no more RCC for the RCD, check for potential refresh of the remote controls
-                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-                }
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // not expected to happen, indicates improper concurrent modification
+                Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
+            }
+            if (topRccChange) {
+                // no more RCC for the RCD, check for potential refresh of the remote controls
+                checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
             }
         }
     }
@@ -1843,7 +1780,7 @@
      * Plug each registered display into the specified client
      * @param rcc, guaranteed non null
      */
-    private void plugRemoteControlDisplaysIntoClient_syncRcStack(IRemoteControlClient rcc) {
+    private void plugRemoteControlDisplaysIntoClient_syncPrs(IRemoteControlClient rcc) {
         final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
         while (displayIterator.hasNext()) {
             final DisplayInfoForServer di = displayIterator.next();
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index 26498ca..edfa36a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -552,29 +552,72 @@
         };
         int availableFormatTag = CameraMetadataNative.getTag("android.scaler.availableFormats");
 
-        // Write
-        mMetadata.set(CameraCharacteristics.SCALER_AVAILABLE_FORMATS, availableFormats);
+        Key<int[]> formatKey = CameraCharacteristics.SCALER_AVAILABLE_FORMATS;
 
-        byte[] availableFormatValues = mMetadata.readValues(availableFormatTag);
+        validateArrayMetadataReadWriteOverride(formatKey, availableFormats,
+                expectedIntValues, availableFormatTag);
 
-        ByteBuffer bf = ByteBuffer.wrap(availableFormatValues).order(ByteOrder.nativeOrder());
+        //
+        // android.scaler.availableStreamConfigurations (int x n x 4 array)
+        //
+        final int OUTPUT = CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT;
+        int[] availableStreamConfigs = new int[] {
+                0x20, 3280, 2464, OUTPUT, // RAW16
+                0x23, 3264, 2448, OUTPUT, // YCbCr_420_888
+                0x23, 3200, 2400, OUTPUT, // YCbCr_420_888
+                0x100, 3264, 2448, OUTPUT, // ImageFormat.JPEG
+                0x100, 3200, 2400, OUTPUT, // ImageFormat.JPEG
+                0x100, 2592, 1944, OUTPUT, // ImageFormat.JPEG
+                0x100, 2048, 1536, OUTPUT, // ImageFormat.JPEG
+                0x100, 1920, 1080, OUTPUT  // ImageFormat.JPEG
+        };
+        int[] expectedAvailableStreamConfigs = new int[] {
+                0x20, 3280, 2464, OUTPUT, // RAW16
+                0x23, 3264, 2448, OUTPUT, // YCbCr_420_888
+                0x23, 3200, 2400, OUTPUT, // YCbCr_420_888
+                0x21, 3264, 2448, OUTPUT, // BLOB
+                0x21, 3200, 2400, OUTPUT, // BLOB
+                0x21, 2592, 1944, OUTPUT, // BLOB
+                0x21, 2048, 1536, OUTPUT, // BLOB
+                0x21, 1920, 1080, OUTPUT  // BLOB
+        };
+        int availableStreamConfigTag =
+                CameraMetadataNative.getTag("android.scaler.availableStreamConfigurations");
 
-        assertEquals(expectedIntValues.length * 4, availableFormatValues.length);
-        for (int i = 0; i < expectedIntValues.length; ++i) {
-            assertEquals(expectedIntValues[i], bf.getInt());
-        }
-        // Read
-        byte[] availableFormatsAsByteArray = new byte[expectedIntValues.length * 4];
-        ByteBuffer availableFormatsByteBuffer =
-                ByteBuffer.wrap(availableFormatsAsByteArray).order(ByteOrder.nativeOrder());
-        for (int value : expectedIntValues) {
-            availableFormatsByteBuffer.putInt(value);
-        }
-        mMetadata.writeValues(availableFormatTag, availableFormatsAsByteArray);
+        Key<int[]> configKey = CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
+        validateArrayMetadataReadWriteOverride(configKey, availableStreamConfigs,
+                expectedAvailableStreamConfigs, availableStreamConfigTag);
 
-        int[] resultFormats = mMetadata.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS);
-        assertNotNull("result available formats shouldn't be null", resultFormats);
-        assertArrayEquals(availableFormats, resultFormats);
+        //
+        // android.scaler.availableMinFrameDurations (int x n x 4 array)
+
+        //
+        long[] availableMinDurations = new long[] {
+                0x20, 3280, 2464, 33333336, // RAW16
+                0x23, 3264, 2448, 33333336, // YCbCr_420_888
+                0x23, 3200, 2400, 33333336, // YCbCr_420_888
+                0x100, 3264, 2448, 33333336, // ImageFormat.JPEG
+                0x100, 3200, 2400, 33333336, // ImageFormat.JPEG
+                0x100, 2592, 1944, 33333336, // ImageFormat.JPEG
+                0x100, 2048, 1536, 33333336, // ImageFormat.JPEG
+                0x100, 1920, 1080, 33333336  // ImageFormat.JPEG
+        };
+        long[] expectedAvailableMinDurations = new long[] {
+                0x20, 3280, 2464, 33333336, // RAW16
+                0x23, 3264, 2448, 33333336, // YCbCr_420_888
+                0x23, 3200, 2400, 33333336, // YCbCr_420_888
+                0x21, 3264, 2448, 33333336, // BLOB
+                0x21, 3200, 2400, 33333336, // BLOB
+                0x21, 2592, 1944, 33333336, // BLOB
+                0x21, 2048, 1536, 33333336, // BLOB
+                0x21, 1920, 1080, 33333336  // BLOB
+        };
+        int availableMinDurationsTag =
+                CameraMetadataNative.getTag("android.scaler.availableMinFrameDurations");
+
+        Key<long[]> durationKey = CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS;
+        validateArrayMetadataReadWriteOverride(durationKey, availableMinDurations,
+                expectedAvailableMinDurations, availableMinDurationsTag);
 
         //
         // android.statistics.faces (Face x n array)
@@ -639,4 +682,59 @@
         }
 
     }
+
+    /**
+     * Validate metadata array tag read/write override.
+     *
+     * <p>Only support long and int array for now, can be easily extend to support other
+     * primitive arrays.</p>
+     */
+    private <T> void validateArrayMetadataReadWriteOverride(Key<T> key, T writeValues,
+            T readValues, int tag) {
+        Class<T> type = key.getType();
+        if (!type.isArray()) {
+            throw new IllegalArgumentException("This function expects an key with array type");
+        } else if (type != int[].class && type != long[].class) {
+            throw new IllegalArgumentException("This function expects long or int array values");
+        }
+
+        // Write
+        mMetadata.set(key, writeValues);
+
+        byte[] readOutValues = mMetadata.readValues(tag);
+
+        ByteBuffer bf = ByteBuffer.wrap(readOutValues).order(ByteOrder.nativeOrder());
+
+        int readValuesLength = Array.getLength(readValues);
+        int readValuesNumBytes = readValuesLength * 4;
+        if (type == long[].class) {
+            readValuesNumBytes = readValuesLength * 8;
+        }
+
+        assertEquals(readValuesNumBytes, readOutValues.length);
+        for (int i = 0; i < readValuesLength; ++i) {
+            if (type == int[].class) {
+                assertEquals(Array.getInt(readValues, i), bf.getInt());
+            } else if (type == long[].class) {
+                assertEquals(Array.getLong(readValues, i), bf.getLong());
+            }
+        }
+
+        // Read
+        byte[] readOutValuesAsByteArray = new byte[readValuesNumBytes];
+        ByteBuffer readOutValuesByteBuffer =
+                ByteBuffer.wrap(readOutValuesAsByteArray).order(ByteOrder.nativeOrder());
+        for (int i = 0; i < readValuesLength; ++i) {
+            if (type == int[].class) {
+                readOutValuesByteBuffer.putInt(Array.getInt(readValues, i));
+            } else if (type == long[].class) {
+                readOutValuesByteBuffer.putLong(Array.getLong(readValues, i));
+            }
+        }
+        mMetadata.writeValues(tag, readOutValuesAsByteArray);
+
+        T result = mMetadata.get(key);
+        assertNotNull(key.getName() + " result shouldn't be null", result);
+        assertArrayEquals(writeValues, result);
+    }
 }
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ce05639..ad10545 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -526,4 +526,12 @@
     <string name="description_direction_up">Slide up for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
     <!-- Description of the left direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
     <string name="description_direction_left">"Slide left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
+
+    <!-- Zen mode: Summary notification content title. [CHAR LIMIT=NONE] -->
+    <plurals name="zen_mode_notification_title">
+        <item quantity="one">Notification hidden</item>
+        <item quantity="other">%d notifications hidden</item>
+    </plurals>
+    <!-- Zen mode: Summary notification content text. [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_notification_text">Touch to show</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index dd75921..64c67b1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -43,6 +43,7 @@
     boolean mVisible;
     boolean mTaskLaunched;
 
+    // Broadcast receiver to handle messages from our RecentsService
     BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -63,6 +64,14 @@
         }
     };
 
+    // Broadcast receiver to handle messages from the system
+    BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            finish();
+        }
+    };
+
     /** Updates the set of recent tasks */
     void updateRecentsTasks() {
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
@@ -111,12 +120,6 @@
         RecentsTaskLoader.initialize(this);
         RecentsConfiguration.reinitialize(this);
 
-        // Set the background dim
-        WindowManager.LayoutParams wlp = getWindow().getAttributes();
-        wlp.dimAmount = Constants.Values.Window.BackgroundDim;
-        getWindow().setAttributes(wlp);
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-
         // Create the view hierarchy
         mRecentsView = new RecentsView(this);
         mRecentsView.setCallbacks(this);
@@ -170,12 +173,37 @@
         Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onResume]", "",
                 Console.AnsiRed);
         super.onResume();
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                "[RecentsActivity|onAttachedToWindow]", "",
+                Console.AnsiRed);
+        super.onAttachedToWindow();
 
         // Register the broadcast receiver to handle messages from our service
         IntentFilter filter = new IntentFilter();
         filter.addAction(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
         filter.addAction(RecentsService.ACTION_FINISH_RECENTS_ACTIVITY);
         registerReceiver(mServiceBroadcastReceiver, filter);
+
+        // Register the broadcast receiver to handle messages when the screen is turned off
+        filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        registerReceiver(mScreenOffReceiver, filter);
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        Console.log(Constants.DebugFlags.App.SystemUIHandshake,
+                "[RecentsActivity|onDetachedFromWindow]", "",
+                Console.AnsiRed);
+        super.onDetachedFromWindow();
+
+        // Unregister any broadcast receivers we have registered
+        unregisterReceiver(mServiceBroadcastReceiver);
+        unregisterReceiver(mScreenOffReceiver);
     }
 
     @Override
@@ -183,9 +211,6 @@
         Console.log(Constants.DebugFlags.App.SystemUIHandshake, "[RecentsActivity|onPause]", "",
                 Console.AnsiRed);
         super.onPause();
-
-        // Unregister any broadcast receivers we have registered
-        unregisterReceiver(mServiceBroadcastReceiver);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 4a0de0b..94a655f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
@@ -62,6 +63,12 @@
         DisplayMetrics dm = res.getDisplayMetrics();
         mDisplayMetrics = dm;
 
+        boolean isLandscape = res.getConfiguration().orientation ==
+                Configuration.ORIENTATION_LANDSCAPE;
+        Console.log(Constants.DebugFlags.UI.MeasureAndLayout,
+                "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
+                Console.AnsiGreen);
+
         displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
         animationPxMovementPerSecond =
                 res.getDimensionPixelSize(R.dimen.recents_animation_movement_in_dps_per_second);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index d661f287..754d956 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -22,7 +22,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
@@ -213,6 +212,7 @@
                                 Console.log(Constants.DebugFlags.App.TaskDataLoader,
                                         "    [TaskResourceLoader|loadThumbnail]",
                                         thumbnail);
+                                thumbnail.setHasAlpha(false);
                                 loadThumbnail = thumbnail;
                                 mThumbnailCache.put(t.key, thumbnail);
                             } else {
@@ -331,13 +331,9 @@
 
         // Create the default assets
         Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        icon.eraseColor(0x00000000);
         mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
-        Canvas c = new Canvas();
-        c.setBitmap(icon);
-        c.drawColor(0x00000000);
-        c.setBitmap(mDefaultThumbnail);
-        c.drawColor(0x00000000);
-        c.setBitmap(null);
+        mDefaultThumbnail.eraseColor(0x00000000);
         mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
         Console.log(Constants.DebugFlags.App.TaskDataLoader,
                 "[RecentsTaskLoader|defaultBitmaps]",
@@ -454,6 +450,7 @@
                             "[RecentsTaskLoader|loadingTaskThumbnail]");
                     task.thumbnail = ssp.getTaskThumbnail(task.key.id);
                     if (task.thumbnail != null) {
+                        task.thumbnail.setHasAlpha(false);
                         mThumbnailCache.put(task.key, task.thumbnail);
                     } else {
                         task.thumbnail = mDefaultThumbnail;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
index efcd948..505238d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
@@ -24,7 +24,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 
@@ -52,9 +51,7 @@
         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
             // Create a dummy icon
             mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
-            Canvas c = new Canvas(mDummyIcon);
-            c.drawColor(0xFF999999);
-            c.setBitmap(null);
+            mDummyIcon.eraseColor(0xFF999999);
         }
     }
 
@@ -117,9 +114,7 @@
         // If we are mocking, then just return a dummy thumbnail
         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
             Bitmap thumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
-            Canvas c = new Canvas(thumbnail);
-            c.drawColor(0xff333333);
-            c.setBitmap(null);
+            thumbnail.eraseColor(0xff333333);
             return thumbnail;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 1ebe231..141d870 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -108,6 +108,7 @@
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int width = MeasureSpec.getSize(widthMeasureSpec);
+        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
         int height = MeasureSpec.getSize(heightMeasureSpec);
         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
 
@@ -118,6 +119,7 @@
 
         // We measure our stack views sans the status bar.  It will handle the nav bar itself.
         RecentsConfiguration config = RecentsConfiguration.getInstance();
+        int childWidth = width - config.systemInsets.right;
         int childHeight = height - config.systemInsets.top;
 
         // Measure each child
@@ -125,7 +127,7 @@
         for (int i = 0; i < childCount; i++) {
             final View child = getChildAt(i);
             if (child.getVisibility() != GONE) {
-                child.measure(widthMeasureSpec,
+                child.measure(MeasureSpec.makeMeasureSpec(childWidth, widthMode),
                         MeasureSpec.makeMeasureSpec(childHeight, heightMode));
             }
         }
@@ -255,11 +257,11 @@
                             | Intent.FLAG_ACTIVITY_TASK_ON_HOME
                             | Intent.FLAG_ACTIVITY_NEW_TASK);
                     try {
+                        UserHandle taskUser = new UserHandle(task.userId);
                         if (opts != null) {
-                            getContext().startActivityAsUser(i, opts.toBundle(),
-                                    new UserHandle(task.userId));
+                            getContext().startActivityAsUser(i, opts.toBundle(), taskUser);
                         } else {
-                            getContext().startActivityAsUser(i, new UserHandle(task.userId));
+                            getContext().startActivityAsUser(i, taskUser);
                         }
                     } catch (ActivityNotFoundException anfe) {
                         Console.logError(getContext(), "Could not start Activity");
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index fa06764..c9a491e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -524,8 +524,8 @@
                 (Constants.Values.TaskStackView.StackPeekHeightPct * mStackRect.height()));
         int size = Math.min(minHeight, Math.min(mStackRect.width(), mStackRect.height()));
         int centerX = mStackRect.centerX();
-        mTaskRect.set(centerX - size / 2, mStackRectSansPeek.top,
-                centerX + size / 2, mStackRectSansPeek.top + size);
+        mTaskRect.set(mStackRect.left, mStackRectSansPeek.top,
+                mStackRect.right, mStackRectSansPeek.top + size);
 
         // Update the scroll bounds
         updateMinMaxScroll(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 844b964..f1299fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -109,9 +109,6 @@
     public static final int EXPANDED_LEAVE_ALONE = -10000;
     public static final int EXPANDED_FULL_OPEN = -10001;
 
-    private static final String EXTRA_INTERCEPT = "android.intercept";
-    private static final float INTERCEPTED_ALPHA = .2f;
-
     protected CommandQueue mCommandQueue;
     protected IStatusBarService mBarService;
     protected H mHandler = createHandler();
@@ -1049,7 +1046,6 @@
         if (DEBUG) {
             Log.d(TAG, "addNotificationViews: added at " + pos);
         }
-        updateInterceptedState(entry);
         updateExpansionStates();
         updateNotificationIcons();
     }
@@ -1082,32 +1078,10 @@
 
     protected void setZenMode(int mode) {
         if (!isDeviceProvisioned()) return;
-        final boolean change = mZenMode != mode;
         mZenMode = mode;
-        final int N = mNotificationData.size();
-        for (int i = 0; i < N; i++) {
-            final NotificationData.Entry entry = mNotificationData.get(i);
-            if (change && !shouldIntercept()) {
-                entry.notification.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false);
-            }
-            updateInterceptedState(entry);
-        }
         updateNotificationIcons();
     }
 
-    private boolean shouldIntercept() {
-        return mZenMode != Settings.Global.ZEN_MODE_OFF;
-    }
-
-    protected boolean shouldIntercept(Notification n) {
-        return shouldIntercept() && n.extras.getBoolean(EXTRA_INTERCEPT);
-    }
-
-    private void updateInterceptedState(NotificationData.Entry entry) {
-        final boolean intercepted = shouldIntercept(entry.notification.getNotification());
-        entry.row.findViewById(R.id.container).setAlpha(intercepted ? INTERCEPTED_ALPHA : 1);
-    }
-
     protected abstract void haltTicker();
     protected abstract void setAreThereNotifications();
     protected abstract void updateNotificationIcons();
@@ -1312,7 +1286,6 @@
         } else {
             entry.content.setOnClickListener(null);
         }
-        updateInterceptedState(entry);
     }
 
     protected void notifyHeadsUpScreenOn(boolean screenOn) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
new file mode 100644
index 0000000..d563968
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar;
+
+import android.app.Notification;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationData.Entry;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+public class InterceptedNotifications {
+    private static final String TAG = "InterceptedNotifications";
+    private static final String EXTRA_INTERCEPT = "android.intercept";
+
+    private final Context mContext;
+    private final PhoneStatusBar mBar;
+    private final ArrayMap<IBinder, StatusBarNotification> mIntercepted
+            = new ArrayMap<IBinder, StatusBarNotification>();
+
+    private Binder mSynKey;
+
+    public InterceptedNotifications(Context context, PhoneStatusBar bar) {
+        mContext = context;
+        mBar = bar;
+    }
+
+    public void releaseIntercepted() {
+        final int n = mIntercepted.size();
+        for (int i = 0; i < n; i++) {
+            final IBinder key = mIntercepted.keyAt(i);
+            final StatusBarNotification sbn = mIntercepted.valueAt(i);
+            sbn.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false);
+            mBar.addNotification(key, sbn);
+        }
+        mIntercepted.clear();
+        updateSyntheticNotification();
+    }
+
+    public boolean tryIntercept(IBinder key, StatusBarNotification notification) {
+        if (!notification.getNotification().extras.getBoolean(EXTRA_INTERCEPT)) return false;
+        mIntercepted.put(key, notification);
+        updateSyntheticNotification();
+        return true;
+    }
+
+    public void remove(IBinder key) {
+        if (mIntercepted.remove(key) != null) {
+            updateSyntheticNotification();
+        }
+    }
+
+    public boolean isSyntheticEntry(Entry ent) {
+        return mSynKey != null && ent.key.equals(mSynKey);
+    }
+
+    public void update(IBinder key, StatusBarNotification notification) {
+        if (mIntercepted.containsKey(key)) {
+            mIntercepted.put(key, notification);
+        }
+    }
+
+    private void updateSyntheticNotification() {
+        if (mIntercepted.isEmpty()) {
+            if (mSynKey != null) {
+                mBar.removeNotification(mSynKey);
+                mSynKey = null;
+            }
+            return;
+        }
+        final Notification n = new Notification.Builder(mContext)
+                .setSmallIcon(R.drawable.stat_sys_zen_limited)
+                .setContentTitle(mContext.getResources().getQuantityString(
+                        R.plurals.zen_mode_notification_title,
+                        mIntercepted.size(), mIntercepted.size()))
+                .setContentText(mContext.getString(R.string.zen_mode_notification_text))
+                .setOngoing(true)
+                .build();
+        final StatusBarNotification sbn = new StatusBarNotification(mContext.getPackageName(),
+                mContext.getBasePackageName(),
+                TAG.hashCode(), TAG, Process.myUid(), Process.myPid(), 0, n,
+                mBar.getCurrentUserHandle());
+        if (mSynKey == null) {
+            mSynKey = new Binder();
+            mBar.addNotification(mSynKey, sbn);
+        } else {
+           mBar.updateNotification(mSynKey, sbn);
+        }
+        final NotificationData.Entry entry = mBar.mNotificationData.findByKey(mSynKey);
+        entry.content.setOnClickListener(mSynClickListener);
+    }
+
+    private final View.OnClickListener mSynClickListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            releaseIntercepted();
+        }
+    };
+}
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 4730f2f..f3dd7e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -59,6 +59,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.service.notification.StatusBarNotification;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -89,11 +90,11 @@
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.InterceptedNotifications;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.StatusBarIconView;
-
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.DateView;
@@ -101,7 +102,6 @@
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.RotationLockController;
-
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import java.io.FileDescriptor;
@@ -347,6 +347,7 @@
         }};
 
     private Runnable mOnFlipRunnable;
+    private InterceptedNotifications mIntercepted;
 
     public void setOnFlipRunnable(Runnable onFlipRunnable) {
         mOnFlipRunnable = onFlipRunnable;
@@ -357,7 +358,11 @@
         super.setZenMode(mode);
         if (mModeIcon == null) return;
         if (!isDeviceProvisioned()) return;
-        mModeIcon.setVisibility(mode != Settings.Global.ZEN_MODE_OFF ? View.VISIBLE : View.GONE);
+        final boolean zen = mode != Settings.Global.ZEN_MODE_OFF;
+        mModeIcon.setVisibility(zen ? View.VISIBLE : View.GONE);
+        if (!zen) {
+            mIntercepted.releaseIntercepted();
+        }
     }
 
     @Override
@@ -365,7 +370,7 @@
         mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
                 .getDefaultDisplay();
         updateDisplaySize();
-
+        mIntercepted = new InterceptedNotifications(mContext, this);
         super.start(); // calls createAndAddWindows()
 
         addNavigationBar();
@@ -931,49 +936,54 @@
         mStatusIcons.removeViewAt(viewIndex);
     }
 
+    public UserHandle getCurrentUserHandle() {
+        return new UserHandle(mCurrentUserId);
+    }
+
     public void addNotification(IBinder key, StatusBarNotification notification) {
         if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore());
         Entry shadeEntry = createNotificationViews(key, notification);
         if (shadeEntry == null) {
             return;
         }
-        if (!shouldIntercept(notification.getNotification())) {
-            if (mUseHeadsUp && shouldInterrupt(notification)) {
-                if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
-                Entry interruptionCandidate = new Entry(key, notification, null);
-                ViewGroup holder = mHeadsUpNotificationView.getHolder();
-                if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
-                    mInterruptingNotificationTime = System.currentTimeMillis();
-                    mInterruptingNotificationEntry = interruptionCandidate;
-                    shadeEntry.setInterruption();
+        if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(key, notification)) {
+            return;
+        }
+        if (mUseHeadsUp && shouldInterrupt(notification)) {
+            if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
+            Entry interruptionCandidate = new Entry(key, notification, null);
+            ViewGroup holder = mHeadsUpNotificationView.getHolder();
+            if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
+                mInterruptingNotificationTime = System.currentTimeMillis();
+                mInterruptingNotificationEntry = interruptionCandidate;
+                shadeEntry.setInterruption();
 
-                    // 1. Populate mHeadsUpNotificationView
-                    mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry);
+                // 1. Populate mHeadsUpNotificationView
+                mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry);
 
-                    // 2. Animate mHeadsUpNotificationView in
-                    mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
+                // 2. Animate mHeadsUpNotificationView in
+                mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
 
-                    // 3. Set alarm to age the notification off
-                    resetHeadsUpDecayTimer();
-                }
-            } else if (notification.getNotification().fullScreenIntent != null) {
-                // Stop screensaver if the notification has a full-screen intent.
-                // (like an incoming phone call)
-                awakenDreams();
+                // 3. Set alarm to age the notification off
+                resetHeadsUpDecayTimer();
+            }
+        } else if (notification.getNotification().fullScreenIntent != null) {
+            // Stop screensaver if the notification has a full-screen intent.
+            // (like an incoming phone call)
+            awakenDreams();
 
-                // not immersive & a full-screen alert should be shown
-                if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
-                try {
-                    notification.getNotification().fullScreenIntent.send();
-                } catch (PendingIntent.CanceledException e) {
-                }
-            } else {
-                // usual case: status bar visible & not immersive
+            // not immersive & a full-screen alert should be shown
+            if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
+            try {
+                notification.getNotification().fullScreenIntent.send();
+            } catch (PendingIntent.CanceledException e) {
+            }
+        } else {
+            // usual case: status bar visible & not immersive
 
-                // show the ticker if there isn't already a heads up
-                if (mInterruptingNotificationEntry == null) {
-                    tick(null, notification, true);
-                }
+            // show the ticker if there isn't already a heads up
+            if (mInterruptingNotificationEntry == null) {
+                tick(null, notification, true);
             }
         }
         addNotificationViews(shadeEntry);
@@ -991,6 +1001,12 @@
         }
     }
 
+    @Override
+    public void updateNotification(IBinder key, StatusBarNotification notification) {
+        super.updateNotification(key, notification);
+        mIntercepted.update(key, notification);
+    }
+
     public void removeNotification(IBinder key) {
         StatusBarNotification old = removeNotificationViews(key);
         if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
@@ -1012,7 +1028,7 @@
                 animateCollapsePanels();
             }
         }
-
+        mIntercepted.remove(key);
         setAreThereNotifications();
     }
 
@@ -1129,7 +1145,7 @@
                 // in "public" mode (atop a secure keyguard), secret notifs are totally hidden
                 continue;
             }
-            if (shouldIntercept(ent.notification.getNotification())) {
+            if (mIntercepted.isSyntheticEntry(ent)) {
                 continue;
             }
             toShow.add(ent.icon);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 972b088..79c4a50 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -151,8 +151,11 @@
 
     private void removeUser(int userId) {
         synchronized (mLock) {
+            UserState userState = mUserStates.get(userId);
+            if (userState == null) {
+                return;
+            }
             // Release created sessions.
-            UserState userState = getUserStateLocked(userId);
             for (SessionState state : userState.sessionStateMap.values()) {
                 if (state.session != null) {
                     try {