Merge "Update SearchIndexablesContract"
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/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
index ad33b6f..bcdfda6 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 92d85d1..7d46cab 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 a747ab6..2e0f509 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);
@@ -267,6 +285,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 94f0683..2d503bf 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_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 32890cf..20a61bf 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,
@@ -203,6 +213,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 },
     { "nSetDisplayListData", "(JJJ)V", (void*) android_view_ThreadedRenderer_setDisplayListData },
     { "nDrawDisplayList", "(JJIIII)V", (void*) android_view_ThreadedRenderer_drawDisplayList },
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/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 fd0fabc..fa0fb8a 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 c231f6f..af35344 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) {
@@ -460,7 +466,7 @@
 
     if (!mCanvas) return;
 
-    makeCurrent();
+    requireSurface();
     Rect dirty;
     mCanvas->invokeFunctors(dirty);
 }
@@ -491,12 +497,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 6f1c37f..9f64944 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 setDisplayListData(RenderNode* displayList, DisplayListData* newData);
     void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters);
@@ -83,7 +84,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 e817e61..a7c955e 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 c50da79..489bf20c1 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 setDisplayListData(RenderNode* renderNode, DisplayListData* newData);
     ANDROID_API void drawDisplayList(RenderNode* displayList,
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/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/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 {