Merge "More debugging for issue #17656716: Unhandled exception in Window Manager" into lmp-dev
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 005baed..3d14c58 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -635,14 +635,7 @@
             if (mIcon != null) {
                 return mIcon;
             }
-            if (mIconFilename != null) {
-                try {
-                    return ActivityManagerNative.getDefault().
-                            getTaskDescriptionIcon(mIconFilename);
-                } catch (RemoteException e) {
-                }
-            }
-            return null;
+            return loadTaskDescriptionIcon(mIconFilename);
         }
 
         /** @hide */
@@ -650,6 +643,23 @@
             return mIconFilename;
         }
 
+        /** @hide */
+        public Bitmap getInMemoryIcon() {
+            return mIcon;
+        }
+
+        /** @hide */
+        public static Bitmap loadTaskDescriptionIcon(String iconFilename) {
+            if (iconFilename != null) {
+                try {
+                    return ActivityManagerNative.getDefault().
+                            getTaskDescriptionIcon(iconFilename);
+                } catch (RemoteException e) {
+                }
+            }
+            return null;
+        }
+
         /**
          * @return The color override on the theme's primary color.
          */
diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java
index ab0fc66..d0aa0fd 100644
--- a/core/java/android/app/FragmentBreadCrumbs.java
+++ b/core/java/android/app/FragmentBreadCrumbs.java
@@ -58,6 +58,8 @@
     private OnBreadCrumbClickListener mOnBreadCrumbClickListener;
 
     private int mGravity;
+    private int mLayoutResId;
+    private int mTextColor;
 
     private static final int DEFAULT_GRAVITY = Gravity.START | Gravity.CENTER_VERTICAL;
 
@@ -103,6 +105,12 @@
 
         mGravity = a.getInt(com.android.internal.R.styleable.FragmentBreadCrumbs_gravity,
                 DEFAULT_GRAVITY);
+        mLayoutResId = a.getResourceId(
+                com.android.internal.R.styleable.FragmentBreadCrumbs_itemLayout,
+                com.android.internal.R.layout.fragment_bread_crumb_item);
+        mTextColor = a.getColor(
+                com.android.internal.R.styleable.FragmentBreadCrumbs_itemColor,
+                0);
 
         a.recycle();
     }
@@ -311,12 +319,11 @@
                 }
             }
             if (i >= numViews) {
-                final View item = mInflater.inflate(
-                        com.android.internal.R.layout.fragment_bread_crumb_item,
-                        this, false);
+                final View item = mInflater.inflate(mLayoutResId, this, false);
                 final TextView text = (TextView) item.findViewById(com.android.internal.R.id.title);
                 text.setText(bse.getBreadCrumbTitle());
                 text.setTag(bse);
+                text.setTextColor(mTextColor);
                 if (i == 0) {
                     item.findViewById(com.android.internal.R.id.left_icon).setVisibility(View.GONE);
                 }
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index b9507d7..68926d0 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -633,7 +633,11 @@
         @Override
         public void onDrained() {
             if (VERBOSE) Log.v(TAG, mIdString + "onIdleDrained");
-            synchronized (CameraCaptureSessionImpl.this) {
+
+            // Take device lock before session lock so that we can call back into device
+            // without causing a deadlock
+            synchronized (mDeviceImpl.mInterfaceLock) {
+                synchronized (CameraCaptureSessionImpl.this) {
                 /*
                  * The device is now IDLE, and has settled. It will not transition to
                  * ACTIVE or BUSY again by itself.
@@ -642,28 +646,31 @@
                  *
                  * This operation is idempotent; a session will not be closed twice.
                  */
-                if (VERBOSE) Log.v(TAG, mIdString + "Session drain complete, skip unconfigure: " +
-                        mSkipUnconfigure);
+                    if (VERBOSE)
+                        Log.v(TAG, mIdString + "Session drain complete, skip unconfigure: " +
+                                mSkipUnconfigure);
 
-                // Fast path: A new capture session has replaced this one; don't unconfigure.
-                if (mSkipUnconfigure) {
-                    mStateCallback.onClosed(CameraCaptureSessionImpl.this);
-                    return;
+                    // Fast path: A new capture session has replaced this one; don't unconfigure.
+                    if (mSkipUnconfigure) {
+                        mStateCallback.onClosed(CameraCaptureSessionImpl.this);
+                        return;
+                    }
+
+                    // Slow path: #close was called explicitly on this session; unconfigure first
+
+                    try {
+                        mUnconfigureDrainer.taskStarted();
+                        mDeviceImpl
+                                .configureOutputsChecked(null); // begin transition to unconfigured
+                    } catch (CameraAccessException e) {
+                        // OK: do not throw checked exceptions.
+                        Log.e(TAG, mIdString + "Exception while configuring outputs: ", e);
+
+                        // TODO: call onError instead of onClosed if this happens
+                    }
+
+                    mUnconfigureDrainer.beginDrain();
                 }
-
-                // Slow path: #close was called explicitly on this session; unconfigure first
-
-                try {
-                    mUnconfigureDrainer.taskStarted();
-                    mDeviceImpl.configureOutputsChecked(null); // begin transition to unconfigured
-                } catch (CameraAccessException e) {
-                    // OK: do not throw checked exceptions.
-                    Log.e(TAG, mIdString + "Exception while configuring outputs: ", e);
-
-                    // TODO: call onError instead of onClosed if this happens
-                }
-
-                mUnconfigureDrainer.beginDrain();
             }
         }
     }
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 2578093..ec450bd1 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -60,7 +60,7 @@
     private ICameraDeviceUser mRemoteDevice;
 
     // Lock to synchronize cross-thread access to device public interface
-    private final Object mInterfaceLock = new Object();
+    final Object mInterfaceLock = new Object(); // access from this class and Session only!
     private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
 
     private final StateCallback mDeviceCallback;
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
index e96c15f..89e2d98 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -73,6 +73,7 @@
         void onError(int errorCode, RequestHolder holder);
         void onConfiguring();
         void onIdle();
+        void onBusy();
         void onCaptureStarted(RequestHolder holder, long timestamp);
         void onCaptureResult(CameraMetadataNative result, RequestHolder holder);
     }
@@ -217,6 +218,20 @@
             }
             Log.i(TAG, "Legacy camera service transitioning to state " + stateName);
         }
+
+        // If we transitioned into a non-IDLE/non-ERROR state then mark the device as busy
+        if(newState != STATE_ERROR && newState != STATE_IDLE) {
+            if (mCurrentState != newState && mCurrentHandler != null &&
+                    mCurrentListener != null) {
+                mCurrentHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mCurrentListener.onBusy();
+                    }
+                });
+            }
+        }
+
         switch(newState) {
             case STATE_ERROR:
                 if (mCurrentState != STATE_ERROR && mCurrentHandler != null &&
diff --git a/core/java/android/hardware/camera2/legacy/GLThreadManager.java b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
index c8e0147..64c532b 100644
--- a/core/java/android/hardware/camera2/legacy/GLThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
@@ -113,6 +113,9 @@
                     case MSG_ALLOW_FRAMES:
                         mDroppingFrames = false;
                         break;
+                    case RequestHandlerThread.MSG_POKE_IDLE_HANDLER:
+                        // OK: Ignore message.
+                        break;
                     default:
                         Log.e(TAG, "Unhandled message " + msg.what + " on GLThread.");
                         break;
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 4587c6f..3a976ba 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -21,6 +21,7 @@
 import android.hardware.Camera;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.impl.CameraDeviceImpl;
 import android.hardware.camera2.impl.CaptureResultExtras;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.params.StreamConfiguration;
@@ -95,7 +96,25 @@
             new CameraDeviceState.CameraDeviceStateListener() {
         @Override
         public void onError(final int errorCode, final RequestHolder holder) {
-            mIdle.open();
+            if (DEBUG) {
+                Log.d(TAG, "onError called, errorCode = " + errorCode);
+            }
+            switch (errorCode) {
+                /*
+                 * Only be considered idle if we hit a fatal error
+                 * and no further requests can be processed.
+                 */
+                case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DISCONNECTED:
+                case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_SERVICE:
+                case CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE: {
+                    mIdle.open();
+
+                    if (DEBUG) {
+                        Log.d(TAG, "onError - opening idle");
+                    }
+                }
+            }
+
             final CaptureResultExtras extras = getExtrasFromRequest(holder);
             mResultHandler.post(new Runnable() {
                 @Override
@@ -124,6 +143,10 @@
 
         @Override
         public void onIdle() {
+            if (DEBUG) {
+                Log.d(TAG, "onIdle called");
+            }
+
             mIdle.open();
 
             mResultHandler.post(new Runnable() {
@@ -143,6 +166,15 @@
         }
 
         @Override
+        public void onBusy() {
+            mIdle.close();
+
+            if (DEBUG) {
+                Log.d(TAG, "onBusy called");
+            }
+        }
+
+        @Override
         public void onCaptureStarted(final RequestHolder holder, final long timestamp) {
             final CaptureResultExtras extras = getExtrasFromRequest(holder);
 
diff --git a/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java b/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java
index 36cd907..0699ffb 100644
--- a/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java
+++ b/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java
@@ -23,6 +23,15 @@
 import android.os.MessageQueue;
 
 public class RequestHandlerThread extends HandlerThread {
+
+    /**
+     * Ensure that the MessageQueue's idle handler gets run by poking the message queue;
+     * normally if the message queue is already idle, the idle handler won't get invoked.
+     *
+     * <p>Users of this handler thread should ignore this message.</p>
+     */
+    public final static int MSG_POKE_IDLE_HANDLER = -1;
+
     private final ConditionVariable mStarted = new ConditionVariable(false);
     private final ConditionVariable mIdle = new ConditionVariable(true);
     private Handler.Callback mCallback;
@@ -86,12 +95,15 @@
 
     // Blocks until thread is idling
     public void waitUntilIdle() {
-        Looper looper = waitAndGetHandler().getLooper();
+        Handler handler = waitAndGetHandler();
+        Looper looper = handler.getLooper();
         if (looper.isIdling()) {
             return;
         }
         mIdle.close();
         looper.getQueue().addIdleHandler(mIdleHandler);
+        // Ensure that the idle handler gets run even if the looper already went idle
+        handler.sendEmptyMessage(MSG_POKE_IDLE_HANDLER);
         if (looper.isIdling()) {
             return;
         }
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index a7ea89c..17ce248 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -864,6 +864,9 @@
                     }
                     resetJpegSurfaceFormats(mCallbackOutputs);
                     break;
+                case RequestHandlerThread.MSG_POKE_IDLE_HANDLER:
+                    // OK: Ignore message.
+                    break;
                 default:
                     throw new AssertionError("Unhandled message " + msg.what +
                             " on RequestThread.");
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 300301b..b492deb 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -702,6 +702,20 @@
     }
 
     /**
+     * Disable NFC hardware.
+     * @hide
+    */
+    @SystemApi
+    public boolean disable(boolean persist) {
+        try {
+            return sService.disable(persist);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return false;
+        }
+    }
+
+    /**
      * Pauses polling for a {@code timeoutInMs} millis. If polling must be resumed before timeout,
      * use {@link #resumePolling()}.
      * @hide
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 4cdafe1..2785ee8 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -665,4 +665,12 @@
     public static final int CRYPT_TYPE_PATTERN = 2;
     /** @hide */
     public static final int CRYPT_TYPE_PIN = 3;
+
+    // Constants for the data available via MountService.getField.
+    /** @hide */
+    public static final String SYSTEM_LOCALE_KEY = "SystemLocale";
+    /** @hide */
+    public static final String OWNER_INFO_KEY = "OwnerInfo";
+    /** @hide */
+    public static final String PATTERN_VISIBLE_KEY = "PatternVisible";
 }
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 23b1e2c..04cd7d5 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -212,6 +212,9 @@
 
     private Button mNextButton;
 
+    private int mPreferenceHeaderItemResId = 0;
+    private boolean mPreferenceHeaderRemoveEmptyIcon = false;
+
     /**
      * The starting request code given out to preference framework.
      */
@@ -258,10 +261,15 @@
         }
 
         private LayoutInflater mInflater;
+        private int mLayoutResId;
+        private boolean mRemoveIconIfEmpty;
 
-        public HeaderAdapter(Context context, List<Header> objects) {
+        public HeaderAdapter(Context context, List<Header> objects, int layoutResId,
+                boolean removeIconBehavior) {
             super(context, 0, objects);
             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            mLayoutResId = layoutResId;
+            mRemoveIconIfEmpty = removeIconBehavior;
         }
 
         @Override
@@ -270,8 +278,7 @@
             View view;
 
             if (convertView == null) {
-                view = mInflater.inflate(com.android.internal.R.layout.preference_header_item,
-                        parent, false);
+                view = mInflater.inflate(mLayoutResId, parent, false);
                 holder = new HeaderViewHolder();
                 holder.icon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
                 holder.title = (TextView) view.findViewById(com.android.internal.R.id.title);
@@ -284,7 +291,16 @@
 
             // All view fields must be updated every time, because the view may be recycled 
             Header header = getItem(position);
-            holder.icon.setImageResource(header.iconRes);
+            if (mRemoveIconIfEmpty) {
+                if (header.iconRes == 0) {
+                    holder.icon.setVisibility(View.GONE);
+                } else {
+                    holder.icon.setVisibility(View.VISIBLE);
+                    holder.icon.setImageResource(header.iconRes);
+                }
+            } else {
+                holder.icon.setImageResource(header.iconRes);
+            }
             holder.title.setText(header.getTitle(getContext().getResources()));
             CharSequence summary = header.getSummary(getContext().getResources());
             if (!TextUtils.isEmpty(summary)) {
@@ -512,7 +528,26 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        setContentView(com.android.internal.R.layout.preference_list_content);
+        // Theming for the PreferenceActivity layout and for the Preference Header(s) layout
+        TypedArray sa = obtainStyledAttributes(null,
+                com.android.internal.R.styleable.PreferenceActivity,
+                com.android.internal.R.attr.preferenceActivityStyle,
+                0);
+
+        final int layoutResId = sa.getResourceId(
+                com.android.internal.R.styleable.PreferenceActivity_layout,
+                com.android.internal.R.layout.preference_list_content);
+
+        mPreferenceHeaderItemResId = sa.getResourceId(
+                com.android.internal.R.styleable.PreferenceActivity_headerLayout,
+                com.android.internal.R.layout.preference_header_item);
+        mPreferenceHeaderRemoveEmptyIcon = sa.getBoolean(
+                com.android.internal.R.styleable.PreferenceActivity_headerRemoveIconIfEmpty,
+                false);
+
+        sa.recycle();
+
+        setContentView(layoutResId);
 
         mListFooter = (FrameLayout)findViewById(com.android.internal.R.id.list_footer);
         mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs_frame);
@@ -582,7 +617,8 @@
                 showBreadCrumbs(initialTitleStr, initialShortTitleStr);
             }
         } else if (mHeaders.size() > 0) {
-            setListAdapter(new HeaderAdapter(this, mHeaders));
+            setListAdapter(new HeaderAdapter(this, mHeaders, mPreferenceHeaderItemResId,
+                    mPreferenceHeaderRemoveEmptyIcon));
             if (!mSinglePane) {
                 // Multi-pane.
                 getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b1d3d45..81fc966 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6096,8 +6096,15 @@
                 if (source != null) {
                     AccessibilityNodeProvider provider = source.getAccessibilityNodeProvider();
                     if (provider != null) {
-                        AccessibilityNodeInfo node = provider.createAccessibilityNodeInfo(
-                                AccessibilityNodeInfo.getVirtualDescendantId(sourceNodeId));
+                        final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
+                                sourceNodeId);
+                        final AccessibilityNodeInfo node;
+                        if (virtualNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+                            node = provider.createAccessibilityNodeInfo(
+                                    AccessibilityNodeProvider.HOST_VIEW_ID);
+                        } else {
+                            node = provider.createAccessibilityNodeInfo(virtualNodeId);
+                        }
                         setAccessibilityFocus(source, node);
                     }
                 }
@@ -6135,8 +6142,14 @@
                         if (provider != null) {
                             final int virtualChildId = AccessibilityNodeInfo.getVirtualDescendantId(
                                     mAccessibilityFocusedVirtualView.getSourceNodeId());
-                            mAccessibilityFocusedVirtualView = provider.createAccessibilityNodeInfo(
-                                    virtualChildId);
+                            if (virtualChildId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+                                mAccessibilityFocusedVirtualView = provider
+                                        .createAccessibilityNodeInfo(
+                                                AccessibilityNodeProvider.HOST_VIEW_ID);
+                            } else {
+                                mAccessibilityFocusedVirtualView = provider
+                                        .createAccessibilityNodeInfo(virtualChildId);
+                            }
                         }
                     }
                 }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 40965f0..55b3ecc 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -48,14 +48,8 @@
     // Returns -1 if nothing could be computed.
     long computeChargeTimeRemaining();
 
-    void addIsolatedUid(int isolatedUid, int appUid);
-    void removeIsolatedUid(int isolatedUid, int appUid);
-
     void noteEvent(int code, String name, int uid);
 
-    void noteProcessStart(String name, int uid);
-    void noteProcessState(String name, int uid, int state);
-    void noteProcessFinish(String name, int uid);
     void noteSyncStart(String name, int uid);
     void noteSyncFinish(String name, int uid);
     void noteJobStart(String name, int uid);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 85b58aa..16fa88e 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -604,7 +604,7 @@
         IMountService mountService = IMountService.Stub.asInterface(service);
         try {
             Log.d(TAG, "Setting owner info");
-            mountService.setField("OwnerInfo", ownerInfo);
+            mountService.setField(StorageManager.OWNER_INFO_KEY, ownerInfo);
         } catch (RemoteException e) {
             Log.e(TAG, "Error changing user info", e);
         }
@@ -1144,7 +1144,7 @@
 
         IMountService mountService = IMountService.Stub.asInterface(service);
         try {
-            mountService.setField("PatternVisible", enabled ? "1" : "0");
+            mountService.setField(StorageManager.PATTERN_VISIBLE_KEY, enabled ? "1" : "0");
         } catch (RemoteException e) {
             Log.e(TAG, "Error changing pattern visible state", e);
         }
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 6796134..8ea28ec 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -478,7 +478,7 @@
 
     NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
 
-    int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
+    jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
 
     struct stat fdStat;
     if (fstat(descriptor, &fdStat) == -1) {
@@ -486,22 +486,16 @@
         return nullObjectReturn("fstat return -1");
     }
 
-    // Duplicate the descriptor here to prevent leaking memory. A leak occurs
-    // if we only close the file descriptor and not the file object it is used to
-    // create.  If we don't explicitly clean up the file (which in turn closes the
-    // descriptor) the buffers allocated internally by fseek will be leaked.
-    int dupDescriptor = dup(descriptor);
+    // Restore the descriptor's offset on exiting this function.
+    AutoFDSeek autoRestore(descriptor);
 
-    FILE* file = fdopen(dupDescriptor, "r");
+    FILE* file = fdopen(descriptor, "r");
     if (file == NULL) {
-        // cleanup the duplicated descriptor since it will not be closed when the
-        // file is cleaned up (fclose).
-        close(dupDescriptor);
         return nullObjectReturn("Could not open file");
     }
 
     SkAutoTUnref<SkFILEStream> fileStream(new SkFILEStream(file,
-                         SkFILEStream::kCallerPasses_Ownership));
+                         SkFILEStream::kCallerRetains_Ownership));
 
     // Use a buffered stream. Although an SkFILEStream can be rewound, this
     // ensures that SkImageDecoder::Factory never rewinds beyond the
diff --git a/core/res/res/layout-xlarge/breadcrumbs_in_fragment_material.xml b/core/res/res/layout-xlarge/breadcrumbs_in_fragment_material.xml
new file mode 100644
index 0000000..6fb6df5
--- /dev/null
+++ b/core/res/res/layout-xlarge/breadcrumbs_in_fragment_material.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+android:id/breadcrumb_section"
+        android:orientation="vertical"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_marginStart="@dimen/preference_breadcrumbs_padding_start_material"
+        android:layout_marginEnd="@dimen/preference_breadcrumbs_padding_end_material"
+    >
+    <android.app.FragmentBreadCrumbs
+            android:id="@android:id/title"
+            android:layout_height="72dip"
+            android:layout_width="match_parent"
+            android:paddingTop="16dip"
+            android:paddingBottom="8dip"
+            android:gravity="center_vertical|start"
+        />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/fragment_bread_crumb_item_material.xml b/core/res/res/layout/fragment_bread_crumb_item_material.xml
new file mode 100644
index 0000000..ee4344f
--- /dev/null
+++ b/core/res/res/layout/fragment_bread_crumb_item_material.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:orientation="horizontal"
+        >
+    <ImageView
+            android:id="@android:id/left_icon"
+            android:src="?attr/dividerVertical"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:scaleType="fitXY"
+            android:layout_marginTop="12dip"
+            android:layout_marginBottom="12dip"
+        />
+
+    <TextView
+            android:id="@+id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:paddingStart="8dip"
+            android:paddingEnd="8dip"
+            android:gravity="center_vertical"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:background="?android:attr/selectableItemBackground"
+            />
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/preference_header_item_material.xml b/core/res/res/layout/preference_header_item_material.xml
new file mode 100644
index 0000000..594189f
--- /dev/null
+++ b/core/res/res/layout/preference_header_item_material.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Layout of a header item in PreferenceActivity. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="48dp"
+    android:background="?android:attr/activatedBackgroundIndicator"
+    android:gravity="center_vertical"
+    android:paddingStart="24dip"
+    android:paddingEnd="?android:attr/scrollbarSize">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="0dip"
+        android:layout_marginEnd="8dip"
+        android:layout_gravity="center" />
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="0dip"
+        android:layout_marginEnd="6dip"
+        android:layout_marginTop="6dip"
+        android:layout_marginBottom="6dip"
+        android:layout_weight="1">
+
+        <TextView android:id="@+android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal" />
+
+        <TextView android:id="@+android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignStart="@android:id/title"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:ellipsize="end"
+            android:maxLines="2" />
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout/preference_list_content_material.xml b/core/res/res/layout/preference_list_content_material.xml
new file mode 100644
index 0000000..7856799
--- /dev/null
+++ b/core/res/res/layout/preference_list_content_material.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/layout/list_content.xml
+**
+** Copyright 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.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent">
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="0px"
+        android:layout_weight="1">
+
+        <LinearLayout
+            style="?attr/preferenceHeaderPanelStyle"
+            android:id="@+id/headers"
+            android:orientation="vertical"
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_weight="@integer/preferences_left_pane_weight"
+            android:background="?attr/windowBackground"
+            android:elevation="4dip" >
+
+            <ListView android:id="@android:id/list"
+                style="?attr/preferenceListStyle"
+                android:layout_width="match_parent"
+                android:layout_height="0px"
+                android:layout_weight="1"
+                android:clipToPadding="false"
+                android:drawSelectorOnTop="false"
+                android:cacheColorHint="@color/transparent"
+                android:listPreferredItemHeight="48dp"
+                android:scrollbarAlwaysDrawVerticalTrack="true" />
+
+            <FrameLayout android:id="@+id/list_footer"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="0" />
+
+        </LinearLayout>
+
+        <LinearLayout
+                android:id="@+id/prefs_frame"
+                style="?attr/preferencePanelStyle"
+                android:layout_width="0px"
+                android:layout_height="match_parent"
+                android:layout_weight="@integer/preferences_right_pane_weight"
+                android:orientation="vertical"
+                android:visibility="gone" >
+
+            <!-- Breadcrumb inserted here, in certain screen sizes. In others, it will be an
+                empty layout or just padding, and PreferenceActivity will put the breadcrumbs in
+                the action bar. -->
+            <include layout="@layout/breadcrumbs_in_fragment_material" />
+
+            <android.preference.PreferenceFrameLayout android:id="@+id/prefs"
+                    android:layout_width="match_parent"
+                    android:layout_height="0dip"
+                    android:layout_weight="1"
+                />
+        </LinearLayout>
+    </LinearLayout>
+
+    <RelativeLayout android:id="@+id/button_bar"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_weight="0"
+        android:visibility="gone">
+
+        <Button android:id="@+id/back_button"
+            android:layout_width="150dip"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:layout_alignParentStart="true"
+            android:text="@string/back_button_label"
+        />
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true">
+
+            <Button android:id="@+id/skip_button"
+                android:layout_width="150dip"
+                android:layout_height="wrap_content"
+                android:layout_margin="5dip"
+                android:text="@string/skip_button_label"
+                android:visibility="gone"
+            />
+
+            <Button android:id="@+id/next_button"
+                android:layout_width="150dip"
+                android:layout_height="wrap_content"
+                android:layout_margin="5dip"
+                android:text="@string/next_button_label"
+            />
+        </LinearLayout>
+    </RelativeLayout>
+</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 90217e5..8ea1814 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -836,6 +836,8 @@
 
         <!-- Default style for PreferenceScreen. -->
         <attr name="preferenceScreenStyle" format="reference" />
+        <!-- Default style for the PreferenceActivity. -->
+        <attr name="preferenceActivityStyle" format="reference" />
         <!-- Default style for Headers pane in PreferenceActivity. -->
         <attr name="preferenceFragmentStyle" format="reference" />
         <!-- Default style for PreferenceCategory. -->
@@ -7265,10 +7267,21 @@
 
     <!-- Base attributes available to PreferenceFragment. -->
     <declare-styleable name="PreferenceFragment">
-        <!-- The layout for the PreferenceFragment. This should rarely need to be changed -->
+        <!-- The layout for the PreferenceFragment. This should rarely need to be changed. -->
         <attr name="layout" />
     </declare-styleable>
 
+    <!-- Base attributes available to PreferenceActivity. -->
+    <declare-styleable name="PreferenceActivity">
+        <!-- The layout for the Preference Activity. This should rarely need to be changed. -->
+        <attr name="layout" />
+        <!-- The layout for the Preference Header. This should rarely need to be changed. -->
+        <attr name="headerLayout" format="reference" />
+        <!-- true if the Icon view will be removed when there is none and thus not showing
+             the fixed margins. -->
+        <attr name="headerRemoveIconIfEmpty" format="boolean" />
+    </declare-styleable>
+
     <!-- Use <code>tts-engine</code> as the root tag of the XML resource that
          describes a text to speech engine implemented as a subclass of
          {@link android.speech.tts.TextToSpeechService}.
@@ -7370,6 +7383,8 @@
     tags. -->
     <declare-styleable name="FragmentBreadCrumbs">
         <attr name="gravity" />
+        <attr name="itemLayout" format="reference" />
+        <attr name="itemColor" format="color|reference" />
     </declare-styleable>
 
     <declare-styleable name="MultiPaneChallengeLayout">
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 9836757..450658e 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -14,9 +14,22 @@
      limitations under the License.
 -->
 <resources>
+    <!-- Preference activity, vertical padding for the header list -->
+    <dimen name="preference_screen_header_vertical_padding_material">8dp</dimen>
+
+    <!-- Preference activity side margins -->
+    <dimen name="preference_screen_side_margin_material">0dp</dimen>
+    <!-- Preference activity side margins negative-->
+    <dimen name="preference_screen_side_margin_negative_material">0dp</dimen>
 
     <!-- Preference fragment padding, sides -->
-    <dimen name="preference_fragment_padding_side_material">0dp</dimen>
+    <dimen name="preference_fragment_padding_side_material">8dp</dimen>
+
+    <!-- Preference breadcrumbs padding, start padding -->
+    <dimen name="preference_breadcrumbs_padding_start_material">12dp</dimen>
+
+    <!-- Preference breadcrumbs padding, end padding -->
+    <dimen name="preference_breadcrumbs_padding_end_material">24dp</dimen>
 
     <dimen name="preference_screen_header_padding_side_material">0dp</dimen>
 
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index c6d0b0b..ba15e9c 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -533,6 +533,8 @@
     <style name="Widget.FragmentBreadCrumbs">
         <item name="padding">4dp</item>
         <item name="animateLayoutChanges">true</item>
+        <item name="itemLayout">@layout/fragment_bread_crumb_item</item>
+        <item name="itemColor">@null</item>
     </style>
 
     <style name="Widget.ImageWell">
@@ -954,6 +956,12 @@
         <item name="paddingEnd">0dp</item>
     </style>
 
+    <style name="PreferenceActivity">
+        <item name="layout">@layout/preference_list_content</item>
+        <item name="headerLayout">@layout/preference_header_item</item>
+        <item name="headerRemoveIconIfEmpty">false</item>
+    </style>
+
     <style name="Preference.Information">
         <item name="layout">@layout/preference_information</item>
         <item name="enabled">false</item>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index e6e5cbb..f9fca00 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -42,6 +42,12 @@
         <item name="paddingEnd">@dimen/preference_fragment_padding_side_material</item>
     </style>
 
+    <style name="PreferenceActivity.Material">
+        <item name="layout">@layout/preference_list_content_material</item>
+        <item name="headerLayout">@layout/preference_header_item_material</item>
+        <item name="headerRemoveIconIfEmpty">true</item>
+    </style>
+
     <style name="Preference.Material.Information">
         <item name="layout">@layout/preference_information_material</item>
         <item name="enabled">false</item>
@@ -93,6 +99,8 @@
 
     <!-- No margins or background by default. Could be different for x-large screens -->
     <style name="PreferencePanel.Material">
+        <item name="layout_marginStart">0dip</item>
+        <item name="layout_marginEnd">0dip</item>
     </style>
 
     <!-- The attributes are overridden here because the x-large or large resources may have
@@ -106,10 +114,10 @@
     </style>
 
     <style name="PreferenceHeaderPanel.Material">
-        <item name="layout_marginStart">@dimen/preference_screen_side_margin</item>
-        <item name="layout_marginEnd">@dimen/preference_screen_side_margin_negative</item>
-        <item name="paddingTop">@dimen/preference_screen_header_vertical_padding</item>
-        <item name="paddingBottom">@dimen/preference_screen_header_vertical_padding</item>
+        <item name="layout_marginStart">@dimen/preference_screen_side_margin_material</item>
+        <item name="layout_marginEnd">@dimen/preference_screen_side_margin_negative_material</item>
+        <item name="paddingTop">@dimen/preference_screen_header_vertical_padding_material</item>
+        <item name="paddingBottom">@dimen/preference_screen_header_vertical_padding_material</item>
     </style>
 
     <style name="PreferenceHeaderList.Material">
@@ -585,6 +593,12 @@
     </style>
 
     <style name="Widget.Material.ExpandableListView.White"/>
+
+    <style name="Widget.Material.FragmentBreadCrumbs" parent="Widget.FragmentBreadCrumbs">
+        <item name="itemLayout">@layout/fragment_bread_crumb_item_material</item>
+        <item name="itemColor">@color/primary_text_default_material_light</item>
+    </style>
+
     <style name="Widget.Material.Gallery" parent="Widget.Gallery"/>
     <style name="Widget.Material.GestureOverlayView" parent="Widget.GestureOverlayView"/>
 
@@ -972,6 +986,9 @@
     <style name="Widget.Material.Light.EditText" parent="Widget.Material.EditText"/>
     <style name="Widget.Material.Light.ExpandableListView" parent="Widget.Material.ExpandableListView"/>
     <style name="Widget.Material.Light.ExpandableListView.White" parent="Widget.Material.ExpandableListView.White"/>
+    <style name="Widget.Material.Light.FragmentBreadCrumbs" parent="Widget.Material.FragmentBreadCrumbs" >
+        <item name="itemColor">@color/primary_text_default_material_dark</item>
+    </style>
     <style name="Widget.Material.Light.Gallery" parent="Widget.Material.Gallery"/>
     <style name="Widget.Material.Light.GestureOverlayView" parent="Widget.Material.GestureOverlayView"/>
     <style name="Widget.Material.Light.GridView" parent="Widget.Material.GridView"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 72f756a..f2b7d7b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2021,6 +2021,7 @@
   <java-symbol type="style" name="TextAppearance.Material.TimePicker.TimeLabel" />
   <java-symbol type="attr" name="seekBarPreferenceStyle" />
   <java-symbol type="style" name="Theme.DeviceDefault.Resolver" />
+  <java-symbol type="attr" name="preferenceActivityStyle" />
   <java-symbol type="attr" name="preferenceFragmentStyle" />
   <java-symbol type="bool" name="skipHoldBeforeMerge" />
   <java-symbol type="bool" name="imsServiceAllowTurnOff" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 3a268a3..7a9e1d5 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -317,6 +317,7 @@
 
         <!-- Preference styles -->
         <item name="preferenceScreenStyle">@style/Preference.PreferenceScreen</item>
+        <item name="preferenceActivityStyle">@style/PreferenceActivity</item>
         <item name="preferenceFragmentStyle">@style/PreferenceFragment</item>
         <item name="preferenceCategoryStyle">@style/Preference.Category</item>
         <item name="preferenceStyle">@style/Preference</item>
diff --git a/core/res/res/values/themes_holo.xml b/core/res/res/values/themes_holo.xml
index 208db97..9c1d0f3 100644
--- a/core/res/res/values/themes_holo.xml
+++ b/core/res/res/values/themes_holo.xml
@@ -308,6 +308,7 @@
 
         <!-- Preference styles -->
         <item name="preferenceScreenStyle">@style/Preference.Holo.PreferenceScreen</item>
+        <item name="preferenceActivityStyle">@style/PreferenceActivity</item>
         <item name="preferenceFragmentStyle">@style/PreferenceFragment.Holo</item>
         <item name="preferenceCategoryStyle">@style/Preference.Holo.Category</item>
         <item name="preferenceStyle">@style/Preference.Holo</item>
@@ -643,6 +644,7 @@
 
         <!-- Preference styles -->
         <item name="preferenceScreenStyle">@style/Preference.Holo.PreferenceScreen</item>
+        <item name="preferenceActivityStyle">@style/PreferenceActivity</item>
         <item name="preferenceFragmentStyle">@style/PreferenceFragment.Holo</item>
         <item name="preferenceCategoryStyle">@style/Preference.Holo.Category</item>
         <item name="preferenceStyle">@style/Preference.Holo</item>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 3c2e302..6420153 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -275,9 +275,11 @@
         <item name="popupMenuStyle">@style/Widget.Material.PopupMenu</item>
         <item name="stackViewStyle">@style/Widget.Material.StackView</item>
         <item name="activityChooserViewStyle">@style/Widget.Material.ActivityChooserView</item>
+        <item name="fragmentBreadCrumbsStyle">@style/Widget.Material.FragmentBreadCrumbs</item>
 
         <!-- Preference styles -->
         <item name="preferenceScreenStyle">@style/Preference.Material.PreferenceScreen</item>
+        <item name="preferenceActivityStyle">@style/PreferenceActivity.Material</item>
         <item name="preferenceFragmentStyle">@style/PreferenceFragment.Material</item>
         <item name="preferenceCategoryStyle">@style/Preference.Material.Category</item>
         <item name="preferenceStyle">@style/Preference.Material</item>
@@ -622,9 +624,11 @@
         <item name="popupMenuStyle">@style/Widget.Material.Light.PopupMenu</item>
         <item name="stackViewStyle">@style/Widget.Material.Light.StackView</item>
         <item name="activityChooserViewStyle">@style/Widget.Material.Light.ActivityChooserView</item>
+        <item name="fragmentBreadCrumbsStyle">@style/Widget.Material.FragmentBreadCrumbs</item>
 
         <!-- Preference styles -->
         <item name="preferenceScreenStyle">@style/Preference.Material.PreferenceScreen</item>
+        <item name="preferenceActivityStyle">@style/PreferenceActivity.Material</item>
         <item name="preferenceFragmentStyle">@style/PreferenceFragment.Material</item>
         <item name="preferenceCategoryStyle">@style/Preference.Material.Category</item>
         <item name="preferenceStyle">@style/Preference.Material</item>
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 14aa570..f7584d8 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -492,7 +492,7 @@
 
     @Override
     public Drawable mutate() {
-        if (!mMutated) {
+        if (!mMutated && super.mutate() == this) {
             final AnimatedStateListState newState = new AnimatedStateListState(mState, this, null);
             setConstantState(newState);
             mMutated = true;
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index df1b126..ad0b415 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -151,7 +151,7 @@
     @Override
     public Drawable mutate() {
         if (!mMutated && super.mutate() == this) {
-            mAnimatedVectorState = new AnimatedVectorDrawableState(mAnimatedVectorState);
+            mAnimatedVectorState.mVectorDrawable.mutate();
             mMutated = true;
         }
         return this;
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 4fd98b7..c7aa98e 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -114,7 +114,8 @@
     /** Current dirty bounds, union of current and previous drawing bounds. */
     private final Rect mDirtyBounds = new Rect();
 
-    private final RippleState mState;
+    /** Mirrors mLayerState with some extra information. */
+    private RippleState mState;
 
     /** The masking layer, e.g. the layer with id R.id.mask. */
     private Drawable mMask;
@@ -885,18 +886,34 @@
         return mState;
     }
 
+    @Override
+    public Drawable mutate() {
+        super.mutate();
+
+        // LayerDrawable creates a new state using createConstantState, so
+        // this should always be a safe cast.
+        mState = (RippleState) mLayerState;
+        return this;
+    }
+
+    @Override
+    RippleState createConstantState(LayerState state, Resources res) {
+        return new RippleState(state, this, res);
+    }
+
     static class RippleState extends LayerState {
         int[] mTouchThemeAttrs;
         ColorStateList mColor = ColorStateList.valueOf(Color.MAGENTA);
         int mMaxRadius = RADIUS_AUTO;
 
-        public RippleState(RippleState orig, RippleDrawable owner, Resources res) {
+        public RippleState(LayerState orig, RippleDrawable owner, Resources res) {
             super(orig, owner, res);
 
-            if (orig != null) {
-                mTouchThemeAttrs = orig.mTouchThemeAttrs;
-                mColor = orig.mColor;
-                mMaxRadius = orig.mMaxRadius;
+            if (orig != null && orig instanceof RippleState) {
+                final RippleState origs = (RippleState) orig;
+                mTouchThemeAttrs = origs.mTouchThemeAttrs;
+                mColor = origs.mColor;
+                mMaxRadius = origs.mMaxRadius;
             }
         }
 
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index bbb0b50..8014837 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -507,9 +507,10 @@
                 pathRenderer.getAlpha());
         pathRenderer.setAlpha(alphaInFloat);
 
-        pathRenderer.mRootName = a.getString(R.styleable.VectorDrawable_name);
-        if (pathRenderer.mRootName != null) {
-            pathRenderer.mVGTargetsMap.put(pathRenderer.mRootName, pathRenderer);
+        final String name = a.getString(R.styleable.VectorDrawable_name);
+        if (name != null) {
+            pathRenderer.mRootName = name;
+            pathRenderer.mVGTargetsMap.put(name, pathRenderer);
         }
     }
 
@@ -1313,9 +1314,15 @@
             // Account for any configuration changes.
             mChangingConfigurations |= a.getChangingConfigurations();
 
-            mPathName = a.getString(R.styleable.VectorDrawableClipPath_name);
-            mNodes = PathParser.createNodesFromPathData(a.getString(
-                    R.styleable.VectorDrawableClipPath_pathData));
+            final String pathName = a.getString(R.styleable.VectorDrawableClipPath_name);
+            if (pathName != null) {
+                mPathName = pathName;
+            }
+
+            final String pathData = a.getString(R.styleable.VectorDrawableClipPath_pathData);
+            if (pathData != null) {
+                mNodes = PathParser.createNodesFromPathData(pathData);
+            }
         }
 
         @Override
@@ -1415,9 +1422,15 @@
             // Extract the theme attributes, if any.
             mThemeAttrs = a.extractThemeAttrs();
 
-            mPathName = a.getString(R.styleable.VectorDrawablePath_name);
-            mNodes = PathParser.createNodesFromPathData(a.getString(
-                    R.styleable.VectorDrawablePath_pathData));
+            final String pathName = a.getString(R.styleable.VectorDrawablePath_name);
+            if (pathName != null) {
+                mPathName = pathName;
+            }
+
+            final String pathData = a.getString(R.styleable.VectorDrawablePath_pathData);
+            if (pathData != null) {
+                mNodes = PathParser.createNodesFromPathData(pathData);
+            }
 
             mFillColor = a.getColor(R.styleable.VectorDrawablePath_fillColor,
                     mFillColor);
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 95c2d61..bb59e5b 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -38,4 +38,7 @@
 
     // This is for the system volume UI only
     void setRemoteVolumeController(in IRemoteVolumeController rvc);
+
+    // For PhoneWindowManager to precheck media keys
+    boolean isGlobalPriorityActive();
 }
\ No newline at end of file
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 5ce7f9f..b37ee6e 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -232,6 +232,10 @@
         }
     }
 
+    public boolean isGlobalPriorityActive() {
+        return mSessionManager.isGlobalPriorityActive();
+    }
+
     public void addRccListener(PendingIntent pi, MediaSession.Callback listener) {
         if (pi == null) {
             Log.w(TAG, "Pending intent was null, can't add rcc listener.");
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 185c6d8..b4fff8f 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -296,6 +296,21 @@
     }
 
     /**
+     * Check if the global priority session is currently active. This can be
+     * used to decide if media keys should be sent to the session or to the app.
+     *
+     * @hide
+     */
+    public boolean isGlobalPriorityActive() {
+        try {
+            return mService.isGlobalPriorityActive();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to check if the global priority is active.", e);
+        }
+        return false;
+    }
+
+    /**
      * Listens for changes to the list of active sessions. This can be added
      * using {@link #addOnActiveSessionsChangedListener}.
      */
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 48450dd..1a44c8c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -908,4 +908,7 @@
 
     <!-- Indication that the current volume and other effects (vibration) are being suppressed by a third party, such as a notification listener. [CHAR LIMIT=30] -->
     <string name="muted_by">Muted by <xliff:g id="third_party">%1$s</xliff:g></string>
+
+    <!-- Accessibility string for current zen mode and selected exit condition. A template that simply concatenates existing mode string and the current condition description. [CHAR LIMIT=20] -->
+    <string name="zen_mode_and_condition"><xliff:g id="zen_mode" example="Priority interruptions only">%1$s</xliff:g>. <xliff:g id="exit_condition" example="For one hour">%2$s</xliff:g></string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 6cc890b..3afdc3d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -18,10 +18,12 @@
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
+import android.app.UiModeManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Configuration;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.hardware.TriggerEvent;
@@ -61,12 +63,14 @@
     private PowerManager mPowerManager;
     private PowerManager.WakeLock mWakeLock;
     private AlarmManager mAlarmManager;
+    private UiModeManager mUiModeManager;
     private boolean mDreaming;
     private boolean mPulsing;
     private boolean mBroadcastReceiverRegistered;
     private boolean mDisplayStateSupported;
     private boolean mNotificationLightOn;
     private boolean mPowerSaveActive;
+    private boolean mCarMode;
     private long mNotificationPulseTime;
     private int mScheduleResetsRemaining;
 
@@ -88,6 +92,7 @@
         pw.print("  mDisplayStateSupported: "); pw.println(mDisplayStateSupported);
         pw.print("  mNotificationLightOn: "); pw.println(mNotificationLightOn);
         pw.print("  mPowerSaveActive: "); pw.println(mPowerSaveActive);
+        pw.print("  mCarMode: "); pw.println(mCarMode);
         pw.print("  mNotificationPulseTime: "); pw.println(mNotificationPulseTime);
         pw.print("  mScheduleResetsRemaining: "); pw.println(mScheduleResetsRemaining);
         mDozeParameters.dump(pw);
@@ -116,6 +121,7 @@
         mWakeLock.setReferenceCounted(true);
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mDisplayStateSupported = mDozeParameters.getDisplayStateSupported();
+        mUiModeManager = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
         turnDisplayOff();
     }
 
@@ -135,12 +141,17 @@
         }
 
         mPowerSaveActive = mHost.isPowerSaveActive();
+        mCarMode = mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR;
         if (DEBUG) Log.d(mTag, "onDreamingStarted canDoze=" + canDoze() + " mPowerSaveActive="
-                + mPowerSaveActive);
+                + mPowerSaveActive + " mCarMode=" + mCarMode);
         if (mPowerSaveActive) {
             finishToSavePower();
             return;
         }
+        if (mCarMode) {
+            finishForCarMode();
+            return;
+        }
 
         mDreaming = true;
         listenForPulseSignals(true);
@@ -221,6 +232,11 @@
         finish();
     }
 
+    private void finishForCarMode() {
+        Log.w(mTag, "Exiting ambient mode, not allowed in car mode");
+        finish();
+    }
+
     private void listenForPulseSignals(boolean listen) {
         if (DEBUG) Log.d(mTag, "listenForPulseSignals: " + listen);
         mSigMotionSensor.setListening(listen);
@@ -233,6 +249,7 @@
         if (listen) {
             final IntentFilter filter = new IntentFilter(PULSE_ACTION);
             filter.addAction(NOTIFICATION_PULSE_ACTION);
+            filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
             mContext.registerReceiver(mBroadcastReceiver, filter);
             mBroadcastReceiverRegistered = true;
         } else {
@@ -341,6 +358,12 @@
                 requestPulse();
                 rescheduleNotificationPulse(mNotificationLightOn);
             }
+            if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
+                mCarMode = true;
+                if (mCarMode && mDreaming) {
+                    finishForCarMode();
+                }
+            }
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index d4b403d..9d4fe66 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -177,16 +177,24 @@
                 if (t != null) {
                     Drawable cachedIcon = mApplicationIconCache.get(t.key);
                     Bitmap cachedThumbnail = mThumbnailCache.get(t.key);
+
                     // Load the application icon if it is stale or we haven't cached one yet
                     if (cachedIcon == null) {
-                        ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(),
-                                t.key.userId);
-                        if (info != null) {
-                            cachedIcon = ssp.getActivityIcon(info, t.key.userId);
+                        cachedIcon = getTaskDescriptionIcon(t.key, t.icon, t.iconFilename, ssp,
+                                mContext.getResources());
+
+                        if (cachedIcon == null) {
+                            ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(),
+                                    t.key.userId);
+                            if (info != null) {
+                                cachedIcon = ssp.getActivityIcon(info, t.key.userId);
+                            }
                         }
+
                         if (cachedIcon == null) {
                             cachedIcon = mDefaultApplicationIcon;
                         }
+
                         // At this point, even if we can't load the icon, we will set the default
                         // icon.
                         mApplicationIconCache.put(t.key, cachedIcon);
@@ -230,6 +238,17 @@
             }
         }
     }
+
+    Drawable getTaskDescriptionIcon(Task.TaskKey taskKey, Bitmap iconBitmap, String iconFilename,
+            SystemServicesProxy ssp, Resources res) {
+        Bitmap tdIcon = iconBitmap != null
+                ? iconBitmap
+                : ActivityManager.TaskDescription.loadTaskDescriptionIcon(iconFilename);
+        if (tdIcon != null) {
+            return ssp.getBadgedIcon(new BitmapDrawable(res, tdIcon), taskKey.userId);
+        }
+        return null;
+    }
 }
 
 /* Recents task loader
@@ -321,15 +340,20 @@
         if (icon != null) {
             return icon;
         }
-        // Return the task description icon if it exists
-        if (td != null && td.getIcon() != null) {
-            icon = ssp.getBadgedIcon(new BitmapDrawable(res, td.getIcon()), taskKey.userId);
-            mApplicationIconCache.put(taskKey, icon);
-            return icon;
-        }
-        // If we are preloading this task, continue to load the activity icon
+
+        // If we are preloading this task, continue to load the task description icon or the
+        // activity icon
         if (preloadTask) {
-            // All short paths failed, load the icon from the activity info and cache it
+
+            // Return and cache the task description icon if it exists
+            Drawable tdDrawable = mLoader.getTaskDescriptionIcon(taskKey, td.getInMemoryIcon(),
+                    td.getIconFilename(), ssp, res);
+            if (tdDrawable != null) {
+                mApplicationIconCache.put(taskKey, tdDrawable);
+                return tdDrawable;
+            }
+
+            // Load the icon from the activity info and cache it
             if (infoHandle.info == null) {
                 infoHandle.info = ssp.getActivityInfo(taskKey.baseIntent.getComponent(),
                         taskKey.userId);
@@ -453,10 +477,17 @@
                 activityInfoCache.put(cnKey, infoHandle);
             }
 
+            Bitmap icon = t.taskDescription != null
+                    ? t.taskDescription.getInMemoryIcon()
+                    : null;
+            String iconFilename = t.taskDescription != null
+                    ? t.taskDescription.getIconFilename()
+                    : null;
+
             // Add the task to the stack
             Task task = new Task(taskKey, (t.id > -1), t.affiliatedTaskId, t.affiliatedTaskColor,
                     activityLabel, activityIcon, activityColor, (i == (taskCount - 1)),
-                    config.lockToAppEnabled);
+                    config.lockToAppEnabled, icon, iconFilename);
 
             if (preloadTask && loadTaskThumbnails) {
                 // Load the thumbnail from the cache if possible
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 406e03f..a7e2b0b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -124,7 +124,8 @@
     public boolean isActive;
     public boolean lockToThisTask;
     public boolean lockToTaskEnabled;
-
+    public Bitmap icon;
+    public String iconFilename;
     TaskCallbacks mCb;
 
     public Task() {
@@ -133,7 +134,8 @@
 
     public Task(TaskKey key, boolean isActive, int taskAffiliation, int taskAffiliationColor,
                 String activityTitle, Drawable activityIcon, int colorPrimary,
-                boolean lockToThisTask, boolean lockToTaskEnabled) {
+                boolean lockToThisTask, boolean lockToTaskEnabled, Bitmap icon,
+                String iconFilename) {
         boolean isInAffiliationGroup = (taskAffiliation != key.id);
         boolean hasAffiliationGroupColor = isInAffiliationGroup && (taskAffiliationColor != 0);
         this.key = key;
@@ -147,6 +149,8 @@
         this.isActive = isActive;
         this.lockToThisTask = lockToTaskEnabled && lockToThisTask;
         this.lockToTaskEnabled = lockToTaskEnabled;
+        this.icon = icon;
+        this.iconFilename = iconFilename;
     }
 
     /** Copies the other task. */
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Interaction.java b/packages/SystemUI/src/com/android/systemui/volume/Interaction.java
new file mode 100644
index 0000000..46eab36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/Interaction.java
@@ -0,0 +1,46 @@
+/*
+ * 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.volume;
+
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnGenericMotionListener;
+import android.view.View.OnTouchListener;
+
+public class Interaction {
+
+    public static void register(View v, final Callback callback) {
+        v.setOnTouchListener(new OnTouchListener() {
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                callback.onInteraction();
+                return false;
+            }
+        });
+        v.setOnGenericMotionListener(new OnGenericMotionListener() {
+            @Override
+            public boolean onGenericMotion(View v, MotionEvent event) {
+                callback.onInteraction();
+                return false;
+            }
+        });
+    }
+
+    public interface Callback {
+        void onInteraction();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
index 66e1e15..f7f5047 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
@@ -80,6 +80,12 @@
         addView(b);
         b.setTag(value);
         b.setOnClickListener(mClick);
+        Interaction.register(b, new Interaction.Callback() {
+            @Override
+            public void onInteraction() {
+                fireInteraction();
+            }
+        });
     }
 
     public void updateLocale() {
@@ -96,6 +102,12 @@
         }
     }
 
+    private void fireInteraction() {
+        if (mCallback != null) {
+            mCallback.onInteraction();
+        }
+    }
+
     private final View.OnClickListener mClick = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
@@ -103,7 +115,7 @@
         }
     };
 
-    public interface Callback {
+    public interface Callback extends Interaction.Callback {
         void onSelected(Object value);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index fa43f32..40bdea2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -42,6 +42,7 @@
 import android.media.session.MediaController;
 import android.media.session.MediaController.PlaybackInfo;
 import android.net.Uri;
+import android.os.Debug;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Vibrator;
@@ -400,11 +401,10 @@
                 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
         mView = window.findViewById(R.id.content);
-        mView.setOnTouchListener(new View.OnTouchListener() {
+        Interaction.register(mView, new Interaction.Callback() {
             @Override
-            public boolean onTouch(View v, MotionEvent event) {
+            public void onInteraction() {
                 resetTimeout();
-                return false;
             }
         });
 
@@ -1382,9 +1382,10 @@
     }
 
     private void resetTimeout() {
+        final boolean touchExploration = mAccessibilityManager.isTouchExplorationEnabled();
         if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
-                + " delay=" + mTimeoutDelay);
-        if (sSafetyWarning == null || !mAccessibilityManager.isTouchExplorationEnabled()) {
+                + " delay=" + mTimeoutDelay + " touchExploration=" + touchExploration);
+        if (sSafetyWarning == null || !touchExploration) {
             removeMessages(MSG_TIMEOUT);
             sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
             removeMessages(MSG_USER_ACTIVITY);
@@ -1393,6 +1394,7 @@
     }
 
     private void forceTimeout(long delay) {
+        if (LOGD) Log.d(mTag, "forceTimeout delay=" + delay + " callers=" + Debug.getCallers(3));
         removeMessages(MSG_TIMEOUT);
         sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index c1681c7..ea431ae 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -135,20 +135,21 @@
             @Override
             public void onClick(View v) {
                 setExpanded(true);
-                fireInteraction();
             }
         });
+        Interaction.register(mZenSubheadCollapsed, mInteractionCallback);
 
         mZenSubheadExpanded = (TextView) findViewById(R.id.zen_subhead_expanded);
+        Interaction.register(mZenSubheadExpanded, mInteractionCallback);
 
         mMoreSettings = findViewById(R.id.zen_more_settings);
         mMoreSettings.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 fireMoreSettings();
-                fireInteraction();
             }
         });
+        Interaction.register(mMoreSettings, mInteractionCallback);
 
         mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
     }
@@ -444,18 +445,22 @@
                         childTag.rb.setChecked(false);
                     }
                     select(tag.condition);
-                    fireInteraction();
+                    announceConditionSelection(tag);
                 }
             }
         });
-        final TextView title = (TextView) row.findViewById(android.R.id.title);
-        if (condition == null) {
-            title.setText(mContext.getString(com.android.internal.R.string.zen_mode_forever));
-        } else {
-            title.setText(condition.summary);
+
+        if (tag.title == null) {
+            tag.title = (TextView) row.findViewById(android.R.id.title);
         }
-        title.setEnabled(enabled);
-        title.setAlpha(enabled ? 1 : .4f);
+        if (condition == null) {
+            tag.title.setText(mContext.getString(com.android.internal.R.string.zen_mode_forever));
+        } else {
+            tag.title.setText(condition.summary);
+        }
+        tag.title.setEnabled(enabled);
+        tag.title.setAlpha(enabled ? 1 : .4f);
+
         final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
         button1.setOnClickListener(new OnClickListener() {
             @Override
@@ -471,11 +476,10 @@
                 onClickTimeButton(row, tag, true /*up*/);
             }
         });
-        title.setOnClickListener(new OnClickListener() {
+        tag.title.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
                 tag.rb.setChecked(true);
-                fireInteraction();
             }
         });
 
@@ -497,6 +501,30 @@
             button1.setVisibility(View.GONE);
             button2.setVisibility(View.GONE);
         }
+        // wire up interaction callbacks for newly-added condition rows
+        if (convertView == null) {
+            Interaction.register(tag.rb, mInteractionCallback);
+            Interaction.register(tag.title, mInteractionCallback);
+            Interaction.register(button1, mInteractionCallback);
+            Interaction.register(button2, mInteractionCallback);
+        }
+    }
+
+    private void announceConditionSelection(ConditionTag tag) {
+        final int zen = getSelectedZen(Global.ZEN_MODE_OFF);
+        String modeText;
+        switch(zen) {
+            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+                modeText = mContext.getString(R.string.zen_important_interruptions);
+                break;
+            case Global.ZEN_MODE_NO_INTERRUPTIONS:
+                modeText = mContext.getString(R.string.zen_no_interruptions);
+                break;
+             default:
+                return;
+        }
+        announceForAccessibility(mContext.getString(R.string.zen_mode_and_condition, modeText,
+                tag.title.getText()));
     }
 
     private void onClickTimeButton(View row, ConditionTag tag, boolean up) {
@@ -530,7 +558,7 @@
         bind(mTimeCondition, row);
         tag.rb.setChecked(true);
         select(mTimeCondition);
-        fireInteraction();
+        announceConditionSelection(tag);
     }
 
     private void select(Condition condition) {
@@ -611,6 +639,7 @@
     // used as the view tag on condition rows
     private static class ConditionTag {
         RadioButton rb;
+        TextView title;
         Condition condition;
     }
 
@@ -691,5 +720,17 @@
                 mController.setZen((Integer) value);
             }
         }
+
+        @Override
+        public void onInteraction() {
+            fireInteraction();
+        }
+    };
+
+    private final Interaction.Callback mInteractionCallback = new Interaction.Callback() {
+        @Override
+        public void onInteraction() {
+            fireInteraction();
+        }
     };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenToast.java b/packages/SystemUI/src/com/android/systemui/volume/ZenToast.java
index 96e2a8e..d887712 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenToast.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenToast.java
@@ -31,6 +31,7 @@
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
 import android.view.WindowManager;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -110,6 +111,17 @@
         message.setText(text);
         final ImageView icon = (ImageView) mZenToast.findViewById(android.R.id.icon);
         icon.setImageResource(iconRes);
+        mZenToast.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+            @Override
+            public void onViewDetachedFromWindow(View v) {
+                // noop
+            }
+
+            @Override
+            public void onViewAttachedToWindow(View v) {
+                mZenToast.announceForAccessibility(message.getText());
+            }
+        });
         mWindowManager.addView(mZenToast, params);
         final int animDuration = res.getInteger(R.integer.zen_toast_animation_duration);
         final int visibleDuration = res.getInteger(R.integer.zen_toast_visible_duration);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 006f5db..f7ed364 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -4093,6 +4093,7 @@
             } else {
                 intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
             }
+            mPowerManager.wakeUp(whenNanos / 1000000);
             mContext.startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
         }
         mCameraLensCoverState = lensCoverState;
@@ -4247,8 +4248,8 @@
         boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
                 || event.isWakeKey();
         if (interactive || (isInjected && !isWakeKey)) {
-            // When the device is interactive or the key is injected pass the key to the
-            // application.
+            // When the device is interactive or the key is injected pass the
+            // key to the application.
             result = ACTION_PASS_TO_USER;
             isWakeKey = false;
         } else if (!interactive && shouldDispatchInputWhenNonInteractive()) {
@@ -4448,16 +4449,6 @@
             case KeyEvent.KEYCODE_MEDIA_PLAY:
             case KeyEvent.KEYCODE_MEDIA_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-                if (down) {
-                    TelecomManager telecomManager = getTelecommService();
-                    if (telecomManager != null) {
-                        if (telecomManager.isInCall()) {
-                            // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
-                            // to avoid music playback.
-                            break;
-                        }
-                    }
-                }
             case KeyEvent.KEYCODE_HEADSETHOOK:
             case KeyEvent.KEYCODE_MUTE:
             case KeyEvent.KEYCODE_MEDIA_STOP:
@@ -4467,6 +4458,11 @@
             case KeyEvent.KEYCODE_MEDIA_RECORD:
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
             case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
+                if (MediaSessionLegacyHelper.getHelper(mContext).isGlobalPriorityActive()) {
+                    // If the global session is active pass all media keys to it
+                    // instead of the active window.
+                    result &= ~ACTION_PASS_TO_USER;
+                }
                 if ((result & ACTION_PASS_TO_USER) == 0) {
                     // Only do this if we would otherwise not pass it to the user. In that
                     // case, the PhoneWindow class will do the same thing, except it will
@@ -5268,6 +5264,7 @@
     @Override
     public void systemBooted() {
         if (mKeyguardDelegate != null) {
+            mKeyguardDelegate.bindService(mContext);
             mKeyguardDelegate.onBootCompleted();
         }
         synchronized (mLock) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
index e9ca5c9..50fe7c7 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
@@ -102,9 +102,12 @@
     };
 
     public KeyguardServiceDelegate(Context context, LockPatternUtils lockPatternUtils) {
+        mScrim = createScrim(context);
+    }
+
+    public void bindService(Context context) {
         Intent intent = new Intent();
         intent.setClassName(KEYGUARD_PACKAGE, KEYGUARD_CLASS);
-        mScrim = createScrim(context);
         if (!context.bindServiceAsUser(intent, mKeyguardConnection,
                 Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
             if (DEBUG) Log.v(TAG, "*** Keyguard: can't bind to " + KEYGUARD_CLASS);
@@ -250,7 +253,6 @@
         if (mKeyguardService != null) {
             mKeyguardService.onSystemReady();
         } else {
-            if (DEBUG) Log.v(TAG, "onSystemReady() called before keyguard service was ready");
             mKeyguardState.systemIsReady = true;
         }
     }
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index ea24d7c..7f24d07 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import android.Manifest;
+import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -28,6 +29,7 @@
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.content.res.Configuration;
 import android.content.res.ObbInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -94,6 +96,7 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -821,6 +824,10 @@
                  */
                 mConnectedSignal.countDown();
 
+                // On an encrypted device we can't see system properties yet, so pull
+                // the system locale out of the mount service.
+                copyLocaleFromMountService();
+
                 // Let package manager load internal ASECs.
                 mPms.scanAvailableAsecs();
 
@@ -830,6 +837,28 @@
         }.start();
     }
 
+    private void copyLocaleFromMountService() {
+        String systemLocale;
+        try {
+            systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY);
+        } catch (RemoteException e) {
+            return;
+        }
+        if (TextUtils.isEmpty(systemLocale)) {
+            return;
+        }
+
+        Slog.d(TAG, "Got locale " + systemLocale + " from mount service");
+        Locale locale = Locale.forLanguageTag(systemLocale);
+        Configuration config = new Configuration();
+        config.setLocale(locale);
+        try {
+            ActivityManagerNative.getDefault().updateConfiguration(config);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error setting system locale from mount service", e);
+        }
+    }
+
     /**
      * Callback from NativeDaemonConnector
      */
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4d2fd4c..d0463b7 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -48,6 +48,8 @@
 import android.graphics.Rect;
 import android.os.BatteryStats;
 import android.os.PersistableBundle;
+import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -1192,6 +1194,7 @@
     static final int ENTER_ANIMATION_COMPLETE_MSG = 44;
     static final int ENABLE_SCREEN_AFTER_BOOT_MSG = 45;
     static final int START_USER_SWITCH_MSG = 46;
+    static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1878,6 +1881,18 @@
                 enableScreenAfterBoot();
                 break;
             }
+            case SEND_LOCALE_TO_MOUNT_DAEMON_MSG: {
+                try {
+                    Locale l = (Locale) msg.obj;
+                    IBinder service = ServiceManager.getService("mount");
+                    IMountService mountService = IMountService.Stub.asInterface(service);
+                    Log.d(TAG, "Storing locale " + l.toLanguageTag() + " for decryption UI");
+                    mountService.setField(StorageManager.SYSTEM_LOCALE_KEY, l.toLanguageTag());
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error storing locale for decryption UI", e);
+                }
+                break;
+            }
             }
         }
     };
@@ -6316,7 +6331,12 @@
                 mHandler.sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
                 // Tell anyone interested that we are done booting!
                 SystemProperties.set("sys.boot_completed", "1");
-                SystemProperties.set("dev.bootcomplete", "1");
+
+                // And trigger dev.bootcomplete if we are not showing encryption progress
+                if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt"))
+                    || "".equals(SystemProperties.get("vold.encrypt_progress"))) {
+                    SystemProperties.set("dev.bootcomplete", "1");
+                }
                 for (int i=0; i<mStartedUsers.size(); i++) {
                     UserStartedState uss = mStartedUsers.valueAt(i);
                     if (uss.mState == UserStartedState.STATE_BOOTING) {
@@ -9542,7 +9562,13 @@
     public void removeContentProviderExternal(String name, IBinder token) {
         enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
             "Do not have permission in call removeContentProviderExternal()");
-        removeContentProviderExternalUnchecked(name, token, UserHandle.getCallingUserId());
+        int userId = UserHandle.getCallingUserId();
+        long ident = Binder.clearCallingIdentity();
+        try {
+            removeContentProviderExternalUnchecked(name, token, userId);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
     }
 
     private void removeContentProviderExternalUnchecked(String name, IBinder token, int userId) {
@@ -16247,6 +16273,8 @@
             SystemProperties.set("persist.sys.language", l.getLanguage());
             SystemProperties.set("persist.sys.country", l.getCountry());
             SystemProperties.set("persist.sys.localevar", l.getVariant());
+
+            mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG, l));
         }
     }
 
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 786196f..a021919 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -123,6 +123,40 @@
         return mStats;
     }
 
+    // These are for direct use by the activity manager...
+
+    void addIsolatedUid(int isolatedUid, int appUid) {
+        synchronized (mStats) {
+            mStats.addIsolatedUidLocked(isolatedUid, appUid);
+        }
+    }
+
+    void removeIsolatedUid(int isolatedUid, int appUid) {
+        synchronized (mStats) {
+            mStats.removeIsolatedUidLocked(isolatedUid, appUid);
+        }
+    }
+
+    void noteProcessStart(String name, int uid) {
+        synchronized (mStats) {
+            mStats.noteProcessStartLocked(name, uid);
+        }
+    }
+
+    void noteProcessState(String name, int uid, int state) {
+        synchronized (mStats) {
+            mStats.noteProcessStateLocked(name, uid, state);
+        }
+    }
+
+    void noteProcessFinish(String name, int uid) {
+        synchronized (mStats) {
+            mStats.noteProcessFinishLocked(name, uid);
+        }
+    }
+
+    // Public interface...
+
     public byte[] getStatistics() {
         mContext.enforceCallingPermission(
                 android.Manifest.permission.BATTERY_STATS, null);
@@ -166,20 +200,6 @@
         }
     }
 
-    public void addIsolatedUid(int isolatedUid, int appUid) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.addIsolatedUidLocked(isolatedUid, appUid);
-        }
-    }
-
-    public void removeIsolatedUid(int isolatedUid, int appUid) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.removeIsolatedUidLocked(isolatedUid, appUid);
-        }
-    }
-
     public void noteEvent(int code, String name, int uid) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -187,27 +207,6 @@
         }
     }
 
-    public void noteProcessStart(String name, int uid) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteProcessStartLocked(name, uid);
-        }
-    }
-
-    public void noteProcessState(String name, int uid, int state) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteProcessStateLocked(name, uid, state);
-        }
-    }
-
-    public void noteProcessFinish(String name, int uid) {
-        enforceCallingPermission();
-        synchronized (mStats) {
-            mStats.noteProcessFinishLocked(name, uid);
-        }
-    }
-
     public void noteSyncStart(String name, int uid) {
         enforceCallingPermission();
         synchronized (mStats) {
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index aaa29fc..02c9fcb5 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -743,6 +743,11 @@
         }
 
         @Override
+        public boolean isGlobalPriorityActive() {
+            return mPriorityStack.isGlobalPriorityActive();
+        }
+
+        @Override
         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index e464be7..c48a075 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -217,6 +217,10 @@
         return null;
     }
 
+    public boolean isGlobalPriorityActive() {
+        return mGlobalPrioritySession == null ? false : mGlobalPrioritySession.isActive();
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0,
                 UserHandle.USER_ALL);