Merge "Make sure we're locked" into nyc-dev
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index a4688d1..f56a6ad 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -274,7 +274,7 @@
                 if (libDir.endsWith(".apk")) {
                     // Avoid opening files we know do not have resources,
                     // like code-only .jar files.
-                    if (assets.addAssetPath(libDir) == 0) {
+                    if (assets.addAssetPathAsSharedLibrary(libDir) == 0) {
                         Log.w(TAG, "Asset path '" + libDir +
                                 "' does not exist or contains no resources.");
                     }
@@ -330,6 +330,22 @@
     }
 
     /**
+     * Finds a cached ResourcesImpl object that matches the given ResourcesKey, or
+     * creates a new one and caches it for future use.
+     * @param key The key to match.
+     * @return a ResourcesImpl object matching the key.
+     */
+    private @NonNull ResourcesImpl findOrCreateResourcesImplForKeyLocked(
+            @NonNull ResourcesKey key) {
+        ResourcesImpl impl = findResourcesImplForKeyLocked(key);
+        if (impl == null) {
+            impl = createResourcesImpl(key);
+            mResourceImpls.put(key, new WeakReference<>(impl));
+        }
+        return impl;
+    }
+
+    /**
      * Find the ResourcesKey that this ResourcesImpl object is associated with.
      * @return the ResourcesKey or null if none was found.
      */
@@ -811,4 +827,75 @@
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
     }
+
+    /**
+     * Appends the library asset path to any ResourcesImpl object that contains the main
+     * assetPath.
+     * @param assetPath The main asset path for which to add the library asset path.
+     * @param libAsset The library asset path to add.
+     */
+    public void appendLibAssetForMainAssetPath(String assetPath, String libAsset) {
+        synchronized (this) {
+            // Record which ResourcesImpl need updating
+            // (and what ResourcesKey they should update to).
+            final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
+
+            final int implCount = mResourceImpls.size();
+            for (int i = 0; i < implCount; i++) {
+                final ResourcesImpl impl = mResourceImpls.valueAt(i).get();
+                final ResourcesKey key = mResourceImpls.keyAt(i);
+                if (impl != null && key.mResDir.equals(assetPath)) {
+                    if (!ArrayUtils.contains(key.mLibDirs, libAsset)) {
+                        final int newLibAssetCount = 1 +
+                                (key.mLibDirs != null ? key.mLibDirs.length : 0);
+                        final String[] newLibAssets = new String[newLibAssetCount];
+                        if (key.mLibDirs != null) {
+                            System.arraycopy(key.mLibDirs, 0, newLibAssets, 0, key.mLibDirs.length);
+                        }
+                        newLibAssets[newLibAssetCount - 1] = libAsset;
+
+                        updatedResourceKeys.put(impl, new ResourcesKey(
+                                key.mResDir,
+                                key.mSplitResDirs,
+                                key.mOverlayDirs,
+                                newLibAssets,
+                                key.mDisplayId,
+                                key.mOverrideConfiguration,
+                                key.mCompatInfo));
+                    }
+                }
+            }
+
+            // Bail early if there is no work to do.
+            if (updatedResourceKeys.isEmpty()) {
+                return;
+            }
+
+            // Update any references to ResourcesImpl that require reloading.
+            final int resourcesCount = mResourceReferences.size();
+            for (int i = 0; i < resourcesCount; i++) {
+                final Resources r = mResourceReferences.get(i).get();
+                if (r != null) {
+                    final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
+                    if (key != null) {
+                        r.setImpl(findOrCreateResourcesImplForKeyLocked(key));
+                    }
+                }
+            }
+
+            // Update any references to ResourcesImpl that require reloading for each Activity.
+            for (ActivityResources activityResources : mActivityResourceReferences.values()) {
+                final int resCount = activityResources.activityResources.size();
+                for (int i = 0; i < resCount; i++) {
+                    final Resources r = activityResources.activityResources.get(i).get();
+                    if (r != null) {
+                        final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
+                        if (key != null) {
+                            r.setImpl(findOrCreateResourcesImplForKeyLocked(key));
+                        }
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 108350a..0881c9c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3646,6 +3646,12 @@
     public static final String SYSTEM_HEALTH_SERVICE = "systemhealth";
 
     /**
+     * Gatekeeper Service.
+     * @hide
+     */
+    public static final String GATEKEEPER_SERVICE = "android.service.gatekeeper.IGateKeeperService";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/hardware/location/ContextHubService.java b/core/java/android/hardware/location/ContextHubService.java
index 8176189..43e596f 100644
--- a/core/java/android/hardware/location/ContextHubService.java
+++ b/core/java/android/hardware/location/ContextHubService.java
@@ -21,12 +21,15 @@
 import android.content.pm.PackageManager;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
 import android.util.Log;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * @hide
@@ -57,8 +60,11 @@
 
     private static final int OS_APP_INSTANCE = -1;
 
+    private static final long APP_ID_ACTIVITY_RECOGNITION = 0x476f6f676c001000L;
+
     private final Context mContext;
-    private final HashMap<Integer, NanoAppInstanceInfo> mNanoAppHash = new HashMap<>();
+    private final ConcurrentHashMap<Integer, NanoAppInstanceInfo> mNanoAppHash =
+            new ConcurrentHashMap<>();
     private final ContextHubInfo[] mContextHubInfo;
     private final RemoteCallbackList<IContextHubCallback> mCallbacksList =
             new RemoteCallbackList<>();
@@ -66,6 +72,18 @@
     private native int nativeSendMessage(int[] header, byte[] data);
     private native ContextHubInfo[] nativeInitialize();
 
+    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
+        @Override
+        public void onVrStateChanged(boolean enabled) {
+            for (NanoAppInstanceInfo app : mNanoAppHash.values()) {
+                if (app.getAppId() == APP_ID_ACTIVITY_RECOGNITION) {
+                    sendVrStateChangeMessageToApp(app, enabled);
+                    break;
+                }
+            }
+        }
+    };
+
     public ContextHubService(Context context) {
         mContext = context;
         mContextHubInfo = nativeInitialize();
@@ -74,6 +92,18 @@
             Log.d(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId()
                   + ", name:  " + mContextHubInfo[i].getName());
         }
+
+        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
+            IVrManager vrManager =
+                    IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager"));
+            if (vrManager != null) {
+                try {
+                    vrManager.registerListener(mVrStateCallbacks);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "VR state listener registration failed", e);
+                }
+            }
+        }
     }
 
     @Override
@@ -277,4 +307,19 @@
 
         return 0;
     }
+
+    private void sendVrStateChangeMessageToApp(NanoAppInstanceInfo app, boolean vrModeEnabled) {
+        int[] msgHeader = new int[MSG_HEADER_SIZE];
+        msgHeader[MSG_FIELD_TYPE] = 0;
+        msgHeader[MSG_FIELD_VERSION] = 0;
+        msgHeader[MSG_FIELD_HUB_HANDLE] = ANY_HUB;
+        msgHeader[MSG_FIELD_APP_INSTANCE] = app.getHandle();
+
+        byte[] data = new byte[1];
+        data[0] = (byte) ((vrModeEnabled) ? 1 : 0);
+        int ret = nativeSendMessage(msgHeader, data);
+        if (ret != 0) {
+            Log.e(TAG, "Couldn't send VR state change notification (" + ret + ")!");
+        }
+    }
 }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index f6e8940..d8be2b6 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -240,6 +240,22 @@
         return new File(getDataDirectory(), "system");
     }
 
+    /**
+     * Returns the base directory for per-user system directory, device encrypted.
+     * {@hide}
+     */
+    public static File getDataSystemDeDirectory() {
+        return buildPath(getDataDirectory(), "system_de");
+    }
+
+    /**
+     * Returns the base directory for per-user system directory, credential encrypted.
+     * {@hide}
+     */
+    public static File getDataSystemCeDirectory() {
+        return buildPath(getDataDirectory(), "system_ce");
+    }
+
     /** {@hide} */
     public static File getDataSystemCeDirectory(int userId) {
         return buildPath(getDataDirectory(), "system_ce", String.valueOf(userId));
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7146448..bcc8d46 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1496,7 +1496,9 @@
     }
 
     /**
-     * Returns information for all users on this device.
+     * Returns information for all users on this device, including ones marked for deletion.
+     * To retrieve only users that are alive, use {@link #getUsers(boolean)}.
+     * <p>
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * @return the list of users that exist on the device.
      * @hide
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 3069e5a..c46acae 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -22,6 +22,7 @@
 import android.graphics.Outline;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.ImageView;
@@ -63,6 +64,33 @@
             }
         }
     };
+    final AccessibilityDelegate mExpandDelegate = new AccessibilityDelegate() {
+
+        @Override
+        public boolean performAccessibilityAction(View host, int action, Bundle args) {
+            if (super.performAccessibilityAction(host, action, args)) {
+                return true;
+            }
+            if (action == AccessibilityNodeInfo.ACTION_COLLAPSE
+                    || action == AccessibilityNodeInfo.ACTION_EXPAND) {
+                mExpandClickListener.onClick(mExpandButton);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(host, info);
+            // Avoid that the button description is also spoken
+            info.setClassName(getClass().getName());
+            if (mExpanded) {
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
+            } else {
+                info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
+            }
+        }
+    };
 
     public NotificationHeaderView(Context context) {
         this(context, null);
@@ -92,6 +120,9 @@
         mAppName = findViewById(com.android.internal.R.id.app_name_text);
         mHeaderText = findViewById(com.android.internal.R.id.header_text);
         mExpandButton = (ImageView) findViewById(com.android.internal.R.id.expand_button);
+        if (mExpandButton != null) {
+            mExpandButton.setAccessibilityDelegate(mExpandDelegate);
+        }
         mIcon = findViewById(com.android.internal.R.id.icon);
         mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
     }
@@ -230,7 +261,7 @@
     public void setOnClickListener(@Nullable OnClickListener l) {
         mExpandClickListener = l;
         setOnTouchListener(mExpandClickListener != null ? mTouchListener : null);
-        setFocusable(l != null);
+        mExpandButton.setOnClickListener(mExpandClickListener);
         updateTouchListener();
     }
 
@@ -380,19 +411,6 @@
         return this;
     }
 
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        if (mExpandClickListener != null) {
-            AccessibilityNodeInfo.AccessibilityAction expand
-                    = new AccessibilityNodeInfo.AccessibilityAction(
-                    AccessibilityNodeInfo.ACTION_CLICK,
-                    getContext().getString(
-                            com.android.internal.R.string.expand_action_accessibility));
-            info.addAction(expand);
-        }
-    }
-
     public ImageView getExpandButton() {
         return mExpandButton;
     }
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 9e73af2..2cdff79 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -21,7 +21,9 @@
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.app.Application;
+import android.app.ResourcesManager;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.os.SystemProperties;
@@ -31,6 +33,8 @@
 import android.view.View;
 import android.view.ViewRootImpl;
 
+import com.android.internal.util.ArrayUtils;
+
 /**
  * Delegate used by the WebView provider implementation to access
  * the required framework functionality needed to implement a {@link WebView}.
@@ -177,7 +181,29 @@
      * Adds the WebView asset path to {@link android.content.res.AssetManager}.
      */
     public void addWebViewAssetPath(Context context) {
-        context.getAssets().addAssetPathAsSharedLibrary(
-                WebViewFactory.getLoadedPackageInfo().applicationInfo.sourceDir);
+        final String newAssetPath = WebViewFactory.getLoadedPackageInfo().applicationInfo.sourceDir;
+
+        final ApplicationInfo appInfo = context.getApplicationInfo();
+        final String[] libs = appInfo.sharedLibraryFiles;
+        if (!ArrayUtils.contains(libs, newAssetPath)) {
+            // Build the new library asset path list.
+            final int newLibAssetsCount = 1 + (libs != null ? libs.length : 0);
+            final String[] newLibAssets = new String[newLibAssetsCount];
+            if (libs != null) {
+                System.arraycopy(libs, 0, newLibAssets, 0, libs.length);
+            }
+            newLibAssets[newLibAssetsCount - 1] = newAssetPath;
+
+            // Update the ApplicationInfo object with the new list.
+            // We know this will persist and future Resources created via ResourcesManager
+            // will include the shared library because this ApplicationInfo comes from the
+            // underlying LoadedApk in ContextImpl, which does not change during the life of the
+            // application.
+            appInfo.sharedLibraryFiles = newLibAssets;
+
+            // Update existing Resources with the WebView library.
+            ResourcesManager.getInstance().appendLibAssetForMainAssetPath(
+                    appInfo.getBaseResourcePath(), newAssetPath);
+        }
     }
 }
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index 04929a7..d0719ee 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -45,6 +45,7 @@
  * default locale.</p>
  */
 public class LocalePickerWithRegion extends ListFragment implements SearchView.OnQueryTextListener {
+    private static final String PARENT_FRAGMENT_NAME = "localeListEditor";
 
     private SuggestedLocaleAdapter mAdapter;
     private LocaleSelectedListener mListener;
@@ -130,11 +131,24 @@
         return true;
     }
 
+    private void returnToParentFrame() {
+        getFragmentManager().popBackStack(PARENT_FRAGMENT_NAME,
+                FragmentManager.POP_BACK_STACK_INCLUSIVE);
+    }
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setHasOptionsMenu(true);
 
+        if (mLocaleList == null) {
+            // The fragment was killed and restored by the FragmentManager.
+            // At this point we have no data, no listener. Just return, to prevend a NPE.
+            // Fixes b/28748150. Created b/29400003 for a cleaner solution.
+            returnToParentFrame();
+            return;
+        }
+
         final boolean countryMode = mParentLocale != null;
         final Locale sortingLocale = countryMode ? mParentLocale.getLocale() : Locale.getDefault();
         mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode);
@@ -197,8 +211,7 @@
             if (mListener != null) {
                 mListener.onLocaleSelected(locale);
             }
-            getFragmentManager().popBackStack("localeListEditor",
-                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
+            returnToParentFrame();
         } else {
             LocalePickerWithRegion selector = LocalePickerWithRegion.createCountryPicker(
                     getContext(), mListener, locale, mTranslatedOnly /* translate only */);
@@ -208,8 +221,7 @@
                         .replace(getId(), selector).addToBackStack(null)
                         .commit();
             } else {
-                getFragmentManager().popBackStack("localeListEditor",
-                        FragmentManager.POP_BACK_STACK_INCLUSIVE);
+                returnToParentFrame();
             }
         }
     }
diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java
new file mode 100644
index 0000000..f4f49b1
--- /dev/null
+++ b/core/java/com/android/internal/widget/NotificationExpandButton.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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.internal.widget;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+
+/**
+ * An expand button in a notification
+ */
+@RemoteViews.RemoteView
+public class NotificationExpandButton extends ImageView {
+    public NotificationExpandButton(Context context) {
+        super(context);
+    }
+
+    public NotificationExpandButton(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public NotificationExpandButton(Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public NotificationExpandButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
+        super.getBoundsOnScreen(outRect, clipToParent);
+        extendRectToMinTouchSize(outRect);
+    }
+
+    private void extendRectToMinTouchSize(Rect rect) {
+        int touchTargetSize = (int) (getResources().getDisplayMetrics().density * 48);
+        rect.left = rect.centerX() - touchTargetSize / 2;
+        rect.right = rect.left + touchTargetSize;
+        rect.top = rect.centerY() - touchTargetSize / 2;
+        rect.bottom = rect.top + touchTargetSize;
+    }
+}
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 38ea92a..38f671c2 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -89,7 +89,7 @@
         android:layout="@layout/notification_template_part_chronometer"
         android:visibility="gone"
         />
-    <ImageView
+    <com.android.internal.widget.NotificationExpandButton
         android:id="@+id/expand_button"
         android:background="@null"
         android:layout_width="wrap_content"
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 9245749..6bc3e4c 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -245,7 +245,7 @@
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakte"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"auf deine Kontakte zugreifen"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Standort"</string>
-    <string name="permgroupdesc_location" msgid="1346617465127855033">"auf den Standort deines Geräts zugreifen"</string>
+    <string name="permgroupdesc_location" msgid="1346617465127855033">"auf den Standort deines Geräts zuzugreifen"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"Kalender"</string>
     <string name="permgroupdesc_calendar" msgid="3889615280211184106">"auf deinen Kalender zugreifen"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
diff --git a/docs/html/preview/behavior-changes.jd b/docs/html/preview/behavior-changes.jd
index b38f1b8..3a37295 100644
--- a/docs/html/preview/behavior-changes.jd
+++ b/docs/html/preview/behavior-changes.jd
@@ -737,7 +737,17 @@
 
   <li>The Work Mode setting controls access to work apps. When work mode is off the
   system launcher indicates work apps are unavailable by greying them out. Enabling
-  work mode again restores normal behavior.
+  work mode again restores normal behavior.</li>
+
+  <li>When installing a PKCS #12 file containing a client certificate chain and
+  the corresponding private key from Settings UI, the CA certificate in the
+  chain is no longer installed to the trusted credentials storage. This does
+  not affect the result of {@link android.security.KeyChain#getCertificateChain
+  KeyChain.getCertificateChain()} when apps attempt to retrieve the client
+  certificate chain later. If required, the CA certificate should be installed
+  to the trusted credentials storage via Settings UI separately, with a
+  DER-encoded format under a .crt or .cer file extension.
+  </li>
 </ul>
 
 <p>
diff --git a/docs/html/training/wearables/watch-faces/service.jd b/docs/html/training/wearables/watch-faces/service.jd
index 20eb0c77..b54f51a 100755
--- a/docs/html/training/wearables/watch-faces/service.jd
+++ b/docs/html/training/wearables/watch-faces/service.jd
@@ -93,24 +93,20 @@
 
 <h3 id="Permissions">Declare Permissions</h3>
 
-<p>Watch faces require the <code>PROVIDE_BACKGROUND</code> and <code>WAKE_LOCK</code> permissions.
-Add the following permissions to the manifest files of both the wearable app and the mobile
-app under the <code>manifest</code> element:</p>
+<p>A watch face requires the <code>WAKE_LOCK</code> permission.
+Add the following permission to the manifest files of both the wearable app
+and the mobile app under the <code>manifest</code> element:</p>
 
 <pre>
 &lt;manifest ...>
     &lt;uses-permission
-        android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
-    &lt;uses-permission
         android:name="android.permission.WAKE_LOCK" />
     ...
 &lt;/manifest>
 </pre>
 
-<p class="caution"><strong>Caution:</strong> The handheld app must include all the permissions
-declared in the wearable app.</p>
-
-
+<p class="caution"><strong>Caution:</strong> The handheld app must include all
+of the permissions declared in the wearable app.</p>
 
 <h2 id="CallbackMethods">Implement the Service and Callback Methods</h2>
 
diff --git a/keystore/java/android/security/GateKeeper.java b/keystore/java/android/security/GateKeeper.java
index c1df28c..7a2cbd0 100644
--- a/keystore/java/android/security/GateKeeper.java
+++ b/keystore/java/android/security/GateKeeper.java
@@ -16,6 +16,7 @@
 
 package android.security;
 
+import android.content.Context;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -32,7 +33,7 @@
 
     public static IGateKeeperService getService() {
         IGateKeeperService service = IGateKeeperService.Stub.asInterface(
-                ServiceManager.getService("android.service.gatekeeper.IGateKeeperService"));
+                ServiceManager.getService(Context.GATEKEEPER_SERVICE));
         if (service == null) {
             throw new IllegalStateException("Gatekeeper service not available");
         }
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index d447a38..ceeb12b 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -6174,7 +6174,7 @@
     if (id >= 256) {
         LOG_ALWAYS_FATAL("Package id out of range");
         return NO_ERROR;
-    } else if (id == 0 || appAsLib || isSystemAsset) {
+    } else if (id == 0 || (id == 0x7f && appAsLib) || isSystemAsset) {
         // This is a library or a system asset, so assign an ID
         id = mNextPackageId++;
     }
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index 13fdb05..bd0cc8c 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -85,7 +85,7 @@
     <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi: signal to‘liq"</string>
     <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"O‘chirilgan ilovalar"</string>
-    <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"O‘chirib yuborilgan ilovalar va foydalanuvchilar"</string>
+    <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"O‘chirib tashlangan ilova va foydalanuvchilar"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB modem"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ixcham hotspot"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth modem"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index ce916cb..37e3c53 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -308,6 +308,13 @@
         }
 
         @Override
+        protected void onPreExecute() {
+            if (sConfigTracker == null || sTileCache == null) {
+                getDashboardCategories();
+            }
+        }
+
+        @Override
         protected void onPostExecute(List<DashboardCategory> dashboardCategories) {
             for (int i = 0; i < dashboardCategories.size(); i++) {
                 DashboardCategory category = dashboardCategories.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
index 913da18..3921a20 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
@@ -30,6 +30,7 @@
 import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
 import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
 import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
+import com.android.systemui.recents.misc.ForegroundThread;
 
 /**
  * An implementation of the system user's Recents interface to be called remotely by secondary
@@ -78,12 +79,16 @@
 
     @Override
     public void updateRecentsVisibility(boolean visible) {
-        mImpl.onVisibilityChanged(mContext, visible);
+        ForegroundThread.getHandler().post(() -> {
+            mImpl.onVisibilityChanged(mContext, visible);
+        });
     }
 
     @Override
     public void startScreenPinning(int taskId) {
-        mImpl.onStartScreenPinning(mContext, taskId);
+        ForegroundThread.getHandler().post(() -> {
+            mImpl.onStartScreenPinning(mContext, taskId);
+        });
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 992b13f..b961055 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -72,7 +72,12 @@
     }
 
     public void showPrompt(int taskId, boolean allowCancel) {
-        clearPrompt();
+        try {
+            clearPrompt();
+        } catch (IllegalArgumentException e) {
+            // If the call to show the prompt fails due to the request window not already being
+            // attached, then just ignore the error since we will be re-adding it below.
+        }
 
         this.taskId = taskId;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 28a6851..e1d4c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -27,6 +27,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.os.Bundle;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -37,11 +38,11 @@
 import android.view.View;
 import android.view.ViewStub;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Chronometer;
 import android.widget.ImageView;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.R;
@@ -50,6 +51,7 @@
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackScrollState;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 import com.android.systemui.statusbar.stack.StackViewState;
@@ -140,15 +142,17 @@
         @Override
         public void onClick(View v) {
             if (!mShowingPublic && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
+                mGroupExpansionChanging = true;
                 final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
                 boolean nowExpanded = mGroupManager.toggleGroupExpansion(mStatusBarNotification);
                 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
-                mGroupExpansionChanging = true;
-                updateBackgroundForGroupState();
                 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER,
                         nowExpanded);
                 logExpansionEvent(true /* userAction */, wasExpanded);
             } else {
+                if (v.isAccessibilityFocused()) {
+                    mPrivateLayout.setFocusOnVisibilityChange();
+                }
                 boolean nowExpanded;
                 if (isPinned()) {
                     nowExpanded = !mExpandedWhenPinned;
@@ -181,6 +185,9 @@
                 }
     };
     private OnClickListener mOnClickListener;
+    private View mChildAfterViewWhenDismissed;
+    private View mGroupParentWhenDismissed;
+    private boolean mRefocusOnDismiss;
 
     public boolean isGroupExpansionChanging() {
         if (isChildInGroup()) {
@@ -717,8 +724,19 @@
         }
     }
 
-    public void setDismissed(boolean dismissed) {
+    public void setDismissed(boolean dismissed, boolean fromAccessibility) {
         mDismissed = dismissed;
+        mGroupParentWhenDismissed = mNotificationParent;
+        mRefocusOnDismiss = fromAccessibility;
+        mChildAfterViewWhenDismissed = null;
+        if (isChildInGroup()) {
+            List<ExpandableNotificationRow> notificationChildren =
+                    mNotificationParent.getNotificationChildren();
+            int i = notificationChildren.indexOf(this);
+            if (i != -1 && i < notificationChildren.size() - 1) {
+                mChildAfterViewWhenDismissed = notificationChildren.get(i + 1);
+            }
+        }
     }
 
     public boolean isDismissed() {
@@ -750,6 +768,14 @@
         return mChildrenContainer;
     }
 
+    public View getChildAfterViewWhenDismissed() {
+        return mChildAfterViewWhenDismissed;
+    }
+
+    public View getGroupParentWhenDismissed() {
+        return mGroupParentWhenDismissed;
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
@@ -1326,8 +1352,11 @@
 
     private void updateClearability() {
         // public versions cannot be dismissed
-        mVetoButton.setVisibility(isClearable() && (!mShowingPublic
-                || !mSensitiveHiddenInGeneral) ? View.VISIBLE : View.GONE);
+        mVetoButton.setVisibility(canViewBeDismissed() ? View.VISIBLE : View.GONE);
+    }
+
+    private boolean canViewBeDismissed() {
+        return isClearable() && (!mShowingPublic || !mSensitiveHiddenInGeneral);
     }
 
     public void makeActionsVisibile() {
@@ -1343,6 +1372,7 @@
         if (mChildrenContainer != null) {
             mChildrenContainer.setChildrenExpanded(expanded);
         }
+        updateBackgroundForGroupState();
         updateClickAndFocus();
     }
 
@@ -1568,6 +1598,32 @@
         }
     }
 
+    @Override
+    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfoInternal(info);
+        if (canViewBeDismissed()) {
+            info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+        if (super.performAccessibilityActionInternal(action, arguments)) {
+            return true;
+        }
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_DISMISS:
+                NotificationStackScrollLayout.performDismiss(this, mGroupManager,
+                        true /* fromAccessibility */);
+                return true;
+        }
+        return false;
+    }
+
+    public boolean shouldRefocusOnDismiss() {
+        return mRefocusOnDismiss || isAccessibilityFocused();
+    }
+
     public interface OnExpandClickListener {
         void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index a11263a..30ac9ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -29,6 +29,7 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
 
 import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.R;
@@ -117,6 +118,7 @@
     private PendingIntent mPreviousHeadsUpRemoteInputIntent;
 
     private int mContentHeightAtAnimationStart = UNDEFINED;
+    private boolean mFocusOnVisibilityChange;
 
 
     public NotificationContentView(Context context, AttributeSet attrs) {
@@ -395,6 +397,19 @@
         }
     }
 
+    private void focusExpandButtonIfNecessary() {
+        if (mFocusOnVisibilityChange) {
+            NotificationHeaderView header = getVisibleNotificationHeader();
+            if (header != null) {
+                ImageView expandButton = header.getExpandButton();
+                if (expandButton != null) {
+                    expandButton.requestAccessibilityFocus();
+                }
+            }
+            mFocusOnVisibilityChange = false;
+        }
+    }
+
     public void setContentHeight(int contentHeight) {
         mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());
         selectLayout(mAnimate /* animate */, false /* force */);
@@ -584,7 +599,8 @@
             updateContentTransformation();
         } else {
             int visibleType = calculateVisibleType();
-            if (visibleType != mVisibleType || force) {
+            boolean changedType = visibleType != mVisibleType;
+            if (changedType || force) {
                 View visibleView = getViewForVisibleType(visibleType);
                 if (visibleView != null) {
                     visibleView.setVisibility(VISIBLE);
@@ -604,6 +620,9 @@
                     updateViewVisibilities(visibleType);
                 }
                 mVisibleType = visibleType;
+                if (changedType) {
+                    focusExpandButtonIfNecessary();
+                }
                 updateBackgroundColor(animate);
             }
         }
@@ -1133,4 +1152,8 @@
             mContentHeightAtAnimationStart = UNDEFINED;
         }
     }
+
+    public void setFocusOnVisibilityChange() {
+        mFocusOnVisibilityChange = true;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 7c391fb..43f847c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -769,7 +769,7 @@
                 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
             }
         }
-        performDismiss(v);
+        performDismiss(v, mGroupManager, false /* fromAccessibility */);
 
         mFalsingManager.onNotificationDismissed();
         if (mFalsingManager.shouldEnforceBouncer()) {
@@ -778,17 +778,18 @@
         }
     }
 
-    private void performDismiss(View v) {
+    public static void performDismiss(View v, NotificationGroupManager groupManager,
+            boolean fromAccessibility) {
         if (v instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-            if (mGroupManager.isOnlyChildInSuppressedGroup(row.getStatusBarNotification())) {
+            if (groupManager.isOnlyChildInGroup(row.getStatusBarNotification())) {
                 ExpandableNotificationRow groupSummary =
-                        mGroupManager.getLogicalGroupSummary(row.getStatusBarNotification());
+                        groupManager.getLogicalGroupSummary(row.getStatusBarNotification());
                 if (groupSummary.isClearable()) {
-                    performDismiss(groupSummary);
+                    performDismiss(groupSummary, groupManager, fromAccessibility);
                 }
             }
-            row.setDismissed(true);
+            row.setDismissed(true, fromAccessibility);
         }
         final View veto = v.findViewById(R.id.veto);
         if (veto != null && veto.getVisibility() != View.GONE) {
@@ -2265,6 +2266,27 @@
 
         // Make sure the clipRect we might have set is removed
         expandableView.setClipTopAmount(0);
+
+        focusNextViewIfFocused(child);
+    }
+
+    private void focusNextViewIfFocused(View view) {
+        if (view instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+            if (row.shouldRefocusOnDismiss()) {
+                View nextView = row.getChildAfterViewWhenDismissed();
+                if (nextView == null) {
+                    View groupParentWhenDismissed = row.getGroupParentWhenDismissed();
+                    nextView = getFirstChildBelowTranlsationY(groupParentWhenDismissed != null
+                            ? groupParentWhenDismissed.getTranslationY()
+                            : view.getTranslationY());
+                }
+                if (nextView != null) {
+                    nextView.requestAccessibilityFocus();
+                }
+            }
+        }
+
     }
 
     private boolean isChildInGroup(View child) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 41d8b4f..130fb7c 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3417,10 +3417,6 @@
                 Slog.w(TAG, "User " + userId + " has no Vpn configuration");
                 return false;
             }
-            // If the current VPN package is the same as the new one, this is a no-op
-            if (TextUtils.equals(packageName, vpn.getAlwaysOnPackage())) {
-                return true;
-            }
             if (!vpn.setAlwaysOnPackage(packageName, lockdown)) {
                 return false;
             }
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index eeb20bf..ef7b787 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -617,6 +617,7 @@
 
     @Override
     public boolean getSeparateProfileChallengeEnabled(int userId) throws RemoteException {
+        checkReadPermission(SEPARATE_PROFILE_CHALLENGE_KEY, userId);
         synchronized (mSeparateChallengeLock) {
             return getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
         }
@@ -625,6 +626,7 @@
     @Override
     public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
             String managedUserPassword) throws RemoteException {
+        checkWritePermission(userId);
         synchronized (mSeparateChallengeLock) {
             setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId);
             if (enabled) {
@@ -672,7 +674,6 @@
     @Override
     public long getLong(String key, long defaultValue, int userId) throws RemoteException {
         checkReadPermission(key, userId);
-
         String value = getStringUnchecked(key, null, userId);
         return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
     }
@@ -680,7 +681,6 @@
     @Override
     public String getString(String key, String defaultValue, int userId) throws RemoteException {
         checkReadPermission(key, userId);
-
         return getStringUnchecked(key, defaultValue, userId);
     }
 
@@ -899,7 +899,7 @@
         }
     }
 
-    public void setLockPatternInternal(String pattern, String savedCredential, int userId)
+    private void setLockPatternInternal(String pattern, String savedCredential, int userId)
             throws RemoteException {
         byte[] currentHandle = getCurrentHandle(userId);
 
@@ -962,7 +962,7 @@
         }
     }
 
-    public void setLockPasswordInternal(String password, String savedCredential, int userId)
+    private void setLockPasswordInternal(String password, String savedCredential, int userId)
             throws RemoteException {
         byte[] currentHandle = getCurrentHandle(userId);
         if (password == null) {
@@ -1156,6 +1156,7 @@
 
     @Override
     public void resetKeyStore(int userId) throws RemoteException {
+        checkWritePermission(userId);
         if (DEBUG) Slog.v(TAG, "Reset keystore for user: " + userId);
         int managedUserId = -1;
         String managedUserDecryptedPassword = null;
@@ -1558,6 +1559,7 @@
             LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
             LockPatternUtils.PASSWORD_HISTORY_KEY,
             LockPatternUtils.PASSWORD_TYPE_KEY,
+            SEPARATE_PROFILE_CHALLENGE_KEY
     };
 
     private static final String[] SETTINGS_TO_BACKUP = new String[] {
@@ -1588,7 +1590,7 @@
         }
 
         final IBinder service =
-            ServiceManager.getService("android.service.gatekeeper.IGateKeeperService");
+            ServiceManager.getService(Context.GATEKEEPER_SERVICE);
         if (service != null) {
             service.linkToDeath(new GateKeeperDiedRecipient(), 0);
             mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 4084542..0cf5172 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -3425,7 +3425,7 @@
     /** {@hide} */
     @NonNull
     public AccountAndUser[] getAllAccounts() {
-        final List<UserInfo> users = getUserManager().getUsers();
+        final List<UserInfo> users = getUserManager().getUsers(true);
         final int[] userIds = new int[users.size()];
         for (int i = 0; i < userIds.length; i++) {
             userIds[i] = users.get(i).id;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 897aa20..09f24ed 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2292,11 +2292,8 @@
                 final ActivityRecord r = (ActivityRecord) msg.obj;
                 final boolean needsVrMode = r != null && r.requestedVrComponent != null;
                 if (needsVrMode) {
-                    VrManagerInternal vrService =
-                            LocalServices.getService(VrManagerInternal.class);
-                    boolean enable = msg.arg1 == 1;
-                    vrService.setVrMode(enable, r.requestedVrComponent, r.userId,
-                            r.info.getComponentName());
+                    applyVrMode(msg.arg1 == 1, r.requestedVrComponent, r.userId,
+                            r.info.getComponentName(), false);
                 }
             } break;
             }
@@ -3084,6 +3081,17 @@
                 mHandler.obtainMessage(VR_MODE_APPLY_IF_NEEDED_MSG, enable ? 1 : 0, 0, r));
     }
 
+    private void applyVrMode(boolean enabled, ComponentName packageName, int userId,
+            ComponentName callingPackage, boolean immediate) {
+        VrManagerInternal vrService =
+                LocalServices.getService(VrManagerInternal.class);
+        if (immediate) {
+            vrService.setVrModeImmediate(enabled, packageName, userId, callingPackage);
+        } else {
+            vrService.setVrMode(enabled, packageName, userId, callingPackage);
+        }
+    }
+
     final void showAskCompatModeDialogLocked(ActivityRecord r) {
         Message msg = Message.obtain();
         msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
@@ -21413,11 +21421,25 @@
         public SleepToken acquireSleepToken(String tag) {
             Preconditions.checkNotNull(tag);
 
+            ComponentName requestedVrService = null;
+            ComponentName callingVrActivity = null;
+            int userId = -1;
+            synchronized (ActivityManagerService.this) {
+                if (mFocusedActivity != null) {
+                    requestedVrService = mFocusedActivity.requestedVrComponent;
+                    callingVrActivity = mFocusedActivity.info.getComponentName();
+                    userId = mFocusedActivity.userId;
+                }
+            }
+
+            if (requestedVrService != null) {
+                applyVrMode(false, requestedVrService, userId, callingVrActivity, true);
+            }
+
             synchronized (ActivityManagerService.this) {
                 SleepTokenImpl token = new SleepTokenImpl(tag);
                 mSleepTokens.add(token);
                 updateSleepIfNeededLocked();
-                applyVrModeIfNeededLocked(mFocusedActivity, false);
                 return token;
             }
         }
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index a2c2040..f26e47e 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -186,9 +186,13 @@
         if (mActivityOptions == null) {
             mActivityOptions = ActivityOptions.makeBasic();
         }
-        // Showing credential confirmation activity in home task to avoid stopping multi-windowed
-        // mode after showing the full-screen credential confirmation activity.
-        mActivityOptions.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId);
+
+        ActivityRecord homeActivityRecord = mSupervisor.getHomeActivity();
+        if (homeActivityRecord != null && homeActivityRecord.task != null) {
+            // Showing credential confirmation activity in home task to avoid stopping multi-windowed
+            // mode after showing the full-screen credential confirmation activity.
+            mActivityOptions.setLaunchTaskId(homeActivityRecord.task.taskId);
+        }
 
         final UserInfo parent = mUserManager.getProfileParent(mUserId);
         mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ebacc71..ede3bda 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -259,28 +259,39 @@
      *
      * @param packageName the package to designate as always-on VPN supplier.
      * @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
+     * @return {@code true} if the package has been set as always-on, {@code false} otherwise.
      */
     public synchronized boolean setAlwaysOnPackage(String packageName, boolean lockdown) {
         enforceControlPermissionOrInternalCaller();
+        if (VpnConfig.LEGACY_VPN.equals(packageName)) {
+            Log.w(TAG, "Not setting legacy VPN \"" + packageName + "\" as always-on.");
+            return false;
+        }
 
-        // Disconnect current VPN.
-        prepareInternal(VpnConfig.LEGACY_VPN);
-
-        // Pre-authorize new always-on VPN package.
         if (packageName != null) {
+            // Pre-authorize new always-on VPN package.
             if (!setPackageAuthorization(packageName, true)) {
                 return false;
             }
-            prepareInternal(packageName);
+            mAlwaysOn = true;
+        } else {
+            packageName = VpnConfig.LEGACY_VPN;
+            mAlwaysOn = false;
         }
 
-        mAlwaysOn = (packageName != null);
         mLockdown = (mAlwaysOn && lockdown);
+        if (!isCurrentPreparedPackage(packageName)) {
+            prepareInternal(packageName);
+        }
         maybeRegisterPackageChangeReceiverLocked(packageName);
         setVpnForcedLocked(mLockdown);
         return true;
     }
 
+    private static boolean isNullOrLegacyVpn(String packageName) {
+        return packageName == null || VpnConfig.LEGACY_VPN.equals(packageName);
+    }
+
     private void unregisterPackageChangeReceiverLocked() {
         // register previous intent filter
         if (mIsPackageIntentReceiverRegistered) {
@@ -293,7 +304,7 @@
         // Unregister IntentFilter listening for previous always-on package change
         unregisterPackageChangeReceiverLocked();
 
-        if (packageName != null) {
+        if (!isNullOrLegacyVpn(packageName)) {
             mIsPackageIntentReceiverRegistered = true;
 
             IntentFilter intentFilter = new IntentFilter();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f8ef3b2..ada0a6b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -19352,10 +19352,18 @@
             if ((flags & StorageManager.FLAG_STORAGE_DE) != 0 && !mOnlyCore) {
                 UserManagerService.enforceSerialNumber(
                         Environment.getDataUserDeDirectory(volumeUuid, userId), userSerial);
+                if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
+                    UserManagerService.enforceSerialNumber(
+                            Environment.getDataSystemDeDirectory(userId), userSerial);
+                }
             }
             if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && !mOnlyCore) {
                 UserManagerService.enforceSerialNumber(
                         Environment.getDataUserCeDirectory(volumeUuid, userId), userSerial);
+                if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
+                    UserManagerService.enforceSerialNumber(
+                            Environment.getDataSystemCeDirectory(userId), userSerial);
+                }
             }
 
             synchronized (mInstallLock) {
@@ -19424,6 +19432,10 @@
                 .listFilesOrEmpty(Environment.getDataUserDeDirectory(volumeUuid)));
         Collections.addAll(files, FileUtils
                 .listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid)));
+        Collections.addAll(files, FileUtils
+                .listFilesOrEmpty(Environment.getDataSystemDeDirectory()));
+        Collections.addAll(files, FileUtils
+                .listFilesOrEmpty(Environment.getDataSystemCeDirectory()));
         for (File file : files) {
             if (!file.isDirectory()) continue;
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a8cf110..a85064b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -65,6 +65,8 @@
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.os.storage.StorageManager;
+import android.security.GateKeeper;
+import android.service.gatekeeper.IGateKeeperService;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -87,6 +89,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.LocalServices;
+import com.android.server.SystemService;
 import com.android.server.am.UserState;
 
 import libcore.io.IoUtils;
@@ -119,6 +122,7 @@
  * </ul>
  */
 public class UserManagerService extends IUserManager.Stub {
+
     private static final String LOG_TAG = "UserManagerService";
     static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
     private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE
@@ -367,6 +371,31 @@
         }
     }
 
+    public static class LifeCycle extends SystemService {
+
+        private UserManagerService mUms;
+
+        /**
+         * @param context
+         */
+        public LifeCycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mUms = UserManagerService.getInstance();
+            publishBinderService(Context.USER_SERVICE, mUms);
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+                mUms.cleanupPartialUsers();
+            }
+        }
+    }
+
     @VisibleForTesting
     UserManagerService(File dataDir) {
         this(null, null, new Object(), dataDir);
@@ -408,25 +437,6 @@
     }
 
     void systemReady() {
-        // Prune out any partially created, partially removed and ephemeral users.
-        ArrayList<UserInfo> partials = new ArrayList<>();
-        synchronized (mUsersLock) {
-            final int userSize = mUsers.size();
-            for (int i = 0; i < userSize; i++) {
-                UserInfo ui = mUsers.valueAt(i).info;
-                if ((ui.partial || ui.guestToRemove || ui.isEphemeral()) && i != 0) {
-                    partials.add(ui);
-                }
-            }
-        }
-        final int partialsSize = partials.size();
-        for (int i = 0; i < partialsSize; i++) {
-            UserInfo ui = partials.get(i);
-            Slog.w(LOG_TAG, "Removing partially created user " + ui.id
-                    + " (name=" + ui.name + ")");
-            removeUserState(ui.id);
-        }
-
         mAppOpsService = IAppOpsService.Stub.asInterface(
                 ServiceManager.getService(Context.APP_OPS_SERVICE));
 
@@ -447,6 +457,27 @@
                 null, mHandler);
     }
 
+    void cleanupPartialUsers() {
+        // Prune out any partially created, partially removed and ephemeral users.
+        ArrayList<UserInfo> partials = new ArrayList<>();
+        synchronized (mUsersLock) {
+            final int userSize = mUsers.size();
+            for (int i = 0; i < userSize; i++) {
+                UserInfo ui = mUsers.valueAt(i).info;
+                if ((ui.partial || ui.guestToRemove || ui.isEphemeral()) && i != 0) {
+                    partials.add(ui);
+                }
+            }
+        }
+        final int partialsSize = partials.size();
+        for (int i = 0; i < partialsSize; i++) {
+            UserInfo ui = partials.get(i);
+            Slog.w(LOG_TAG, "Removing partially created user " + ui.id
+                    + " (name=" + ui.name + ")");
+            removeUserState(ui.id);
+        }
+    }
+
     @Override
     public String getUserAccount(int userId) {
         checkManageUserAndAcrossUsersFullPermission("get user account");
@@ -2472,8 +2503,23 @@
                 "Destroying key for user " + userHandle + " failed, continuing anyway", e);
         }
 
+        // Cleanup gatekeeper secure user id
+        try {
+            final IGateKeeperService gk = GateKeeper.getService();
+            if (gk != null) {
+                gk.clearSecureUserId(userHandle);
+            }
+        } catch (Exception ex) {
+            Slog.w(LOG_TAG, "unable to clear GK secure user id");
+        }
+
         // Cleanup package manager settings
         mPm.cleanUpUser(this, userHandle);
+
+        // Clean up all data before removing metadata
+        mPm.destroyUserData(userHandle,
+                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+
         // Remove this user from the list
         synchronized (mUsersLock) {
             mUsers.remove(userHandle);
@@ -2496,12 +2542,6 @@
         AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
         userFile.delete();
         updateUserIds();
-
-        // Now that we've purged all the metadata above, destroy the actual data
-        // on disk; if we battery pull in here we'll finish cleaning up when
-        // reconciling after reboot.
-        mPm.destroyUserData(userHandle,
-                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
     }
 
     private void sendProfileRemovedBroadcast(int parentUserId, int removedUserId) {
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 1bbb9f5..ad87a88 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -43,6 +43,9 @@
 
     /**
      * Set the current VR mode state.
+     * <p/>
+     * This may delay the mode change slightly during application transitions to avoid frequently
+     * tearing down VrListenerServices unless necessary.
      *
      * @param enabled {@code true} to enable VR mode.
      * @param packageName The package name of the requested VrListenerService to bind.
@@ -52,6 +55,18 @@
     public abstract void setVrMode(boolean enabled, @NonNull ComponentName packageName,
             int userId, @NonNull ComponentName calling);
 
+    /**
+     * Set the current VR mode state immediately.
+     *
+     * @param enabled {@code true} to enable VR mode.
+     * @param packageName The package name of the requested VrListenerService to bind.
+     * @param userId the user requesting the VrListenerService component.
+     * @param calling the component currently using VR mode, or null to leave unchanged.
+     */
+    public abstract void setVrModeImmediate(boolean enabled, @NonNull ComponentName packageName,
+            int userId, @NonNull ComponentName calling);
+
+
    /**
     * Return NO_ERROR if the given package is installed on the device and enabled as a
     * VrListenerService for the given current user, or a negative error code indicating a failure.
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index b4c4bd8..5fefd4c 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -218,7 +218,6 @@
                     String packageName = mNotificationAccessPackageToUserId.keyAt(i);
                     revokeNotificationListenerAccess(packageName, grantUserId);
                     revokeNotificationPolicyAccess(packageName);
-                    revokeCoarseLocationPermissionIfNeeded(packageName, grantUserId);
                     mNotificationAccessPackageToUserId.removeAt(i);
                 }
             }
@@ -227,7 +226,6 @@
                 if (!packageNames.contains(pkg)) {
                     revokeNotificationListenerAccess(pkg, currentUserId);
                     revokeNotificationPolicyAccess(pkg);
-                    revokeCoarseLocationPermissionIfNeeded(pkg, currentUserId);
                     mNotificationAccessPackageToUserId.remove(pkg);
                 }
             }
@@ -235,7 +233,6 @@
                 if (!allowed.contains(pkg)) {
                     grantNotificationPolicyAccess(pkg);
                     grantNotificationListenerAccess(pkg, currentUserId);
-                    grantCoarseLocationPermissionIfNeeded(pkg, currentUserId);
                     mNotificationAccessPackageToUserId.put(pkg, currentUserId);
                 }
             }
@@ -373,7 +370,13 @@
         @Override
         public void setVrMode(boolean enabled, ComponentName packageName, int userId,
                 ComponentName callingPackage) {
-            VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage);
+            VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage, false);
+        }
+
+        @Override
+        public void setVrModeImmediate(boolean enabled, ComponentName packageName, int userId,
+                ComponentName callingPackage) {
+            VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage, true);
         }
 
         @Override
@@ -754,22 +757,6 @@
                 flatSettings, userId);
     }
 
-    private void grantCoarseLocationPermissionIfNeeded(String pkg, int userId) {
-        // Don't clobber the user if permission set in current state explicitly
-        if (!isPermissionUserUpdated(Manifest.permission.ACCESS_COARSE_LOCATION, pkg, userId)) {
-            mContext.getPackageManager().grantRuntimePermission(pkg,
-                    Manifest.permission.ACCESS_COARSE_LOCATION, new UserHandle(userId));
-        }
-    }
-
-    private void revokeCoarseLocationPermissionIfNeeded(String pkg, int userId) {
-        // Don't clobber the user if permission set in current state explicitly
-        if (!isPermissionUserUpdated(Manifest.permission.ACCESS_COARSE_LOCATION, pkg, userId)) {
-            mContext.getPackageManager().revokeRuntimePermission(pkg,
-                    Manifest.permission.ACCESS_COARSE_LOCATION, new UserHandle(userId));
-        }
-    }
-
     private boolean isPermissionUserUpdated(String permission, String pkg, int userId) {
         final int flags = mContext.getPackageManager().getPermissionFlags(
                 permission, pkg, new UserHandle(userId));
@@ -916,11 +903,11 @@
      */
 
     private void setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
-            int userId, @NonNull ComponentName callingPackage) {
+            int userId, @NonNull ComponentName callingPackage, boolean immediate) {
 
         synchronized (mLock) {
 
-            if (!enabled && mCurrentVrService != null) {
+            if (!enabled && mCurrentVrService != null && !immediate) {
                 // If we're transitioning out of VR mode, delay briefly to avoid expensive HAL calls
                 // and service bind/unbind in case we are immediately switching to another VR app.
                 if (mPendingState == null) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 1ae1a77..b53933e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -120,7 +120,7 @@
             // First, try to read from the legacy file.
             final File legacy = getLegacyConfigFileWithTestOverride();
 
-            final List<UserInfo> users = mUserManager.getUsers();
+            final List<UserInfo> users = mUserManager.getUsers(true);
 
             if (readLegacyOwnerFileLocked(legacy)) {
                 if (DEBUG) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 476a559..f59b2ff 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -470,7 +470,7 @@
         }
 
         traceBeginAndSlog("StartUserManagerService");
-        ServiceManager.addService(Context.USER_SERVICE, UserManagerService.getInstance());
+        mSystemServiceManager.startService(UserManagerService.LifeCycle.class);
         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
 
         // Initialize attribute cache used to cache resources from packages.
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 9fcbfe3..0227d27 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -82,6 +82,7 @@
     private StatusHints mStatusHints;
     private Bundle mExtras;
     private Set<String> mPreviousExtraKeys;
+    private final Object mExtrasLock = new Object();
 
     private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
         @Override
@@ -686,32 +687,35 @@
      * @param extras The extras associated with this {@code Conference}.
      */
     public final void setExtras(@Nullable Bundle extras) {
-        // Add/replace any new or changed extras values.
-        putExtras(extras);
+        // Keeping putExtras and removeExtras in the same lock so that this operation happens as a
+        // block instead of letting other threads put/remove while this method is running.
+        synchronized (mExtrasLock) {
+            // Add/replace any new or changed extras values.
+            putExtras(extras);
+            // If we have used "setExtras" in the past, compare the key set from the last invocation
+            // to the current one and remove any keys that went away.
+            if (mPreviousExtraKeys != null) {
+                List<String> toRemove = new ArrayList<String>();
+                for (String oldKey : mPreviousExtraKeys) {
+                    if (extras == null || !extras.containsKey(oldKey)) {
+                        toRemove.add(oldKey);
+                    }
+                }
 
-        // If we have used "setExtras" in the past, compare the key set from the last invocation to
-        // the current one and remove any keys that went away.
-        if (mPreviousExtraKeys != null) {
-            List<String> toRemove = new ArrayList<String>();
-            for (String oldKey : mPreviousExtraKeys) {
-                if (extras == null || !extras.containsKey(oldKey)) {
-                    toRemove.add(oldKey);
+                if (!toRemove.isEmpty()) {
+                    removeExtras(toRemove);
                 }
             }
 
-            if (!toRemove.isEmpty()) {
-                removeExtras(toRemove);
+            // Track the keys the last time set called setExtras.  This way, the next time setExtras
+            // is called we can see if the caller has removed any extras values.
+            if (mPreviousExtraKeys == null) {
+                mPreviousExtraKeys = new ArraySet<String>();
             }
-        }
-
-        // Track the keys the last time set called setExtras.  This way, the next time setExtras is
-        // called we can see if the caller has removed any extras values.
-        if (mPreviousExtraKeys == null) {
-            mPreviousExtraKeys = new ArraySet<String>();
-        }
-        mPreviousExtraKeys.clear();
-        if (extras != null) {
-            mPreviousExtraKeys.addAll(extras.keySet());
+            mPreviousExtraKeys.clear();
+            if (extras != null) {
+                mPreviousExtraKeys.addAll(extras.keySet());
+            }
         }
     }
 
@@ -730,13 +734,19 @@
             return;
         }
 
-        if (mExtras == null) {
-            mExtras = new Bundle();
+        // Creating a Bundle clone so we don't have to synchronize on mExtrasLock while calling
+        // onExtrasChanged.
+        Bundle listenersBundle;
+        synchronized (mExtrasLock) {
+            if (mExtras == null) {
+                mExtras = new Bundle();
+            }
+            mExtras.putAll(extras);
+            listenersBundle = new Bundle(mExtras);
         }
-        mExtras.putAll(extras);
 
         for (Listener l : mListeners) {
-            l.onExtrasChanged(this, extras);
+            l.onExtrasChanged(this, new Bundle(listenersBundle));
         }
     }
 
@@ -790,17 +800,17 @@
             return;
         }
 
-        if (mExtras != null) {
-            for (String key : keys) {
-                mExtras.remove(key);
-            }
-            if (mExtras.size() == 0) {
-                mExtras = null;
+        synchronized (mExtrasLock) {
+            if (mExtras != null) {
+                for (String key : keys) {
+                    mExtras.remove(key);
+                }
             }
         }
 
+        List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
         for (Listener l : mListeners) {
-            l.onExtrasRemoved(this, keys);
+            l.onExtrasRemoved(this, unmodifiableKeys);
         }
     }
 
@@ -833,7 +843,13 @@
      * @hide
      */
     final void handleExtrasChanged(Bundle extras) {
-        mExtras = extras;
-        onExtrasChanged(mExtras);
+        Bundle b = null;
+        synchronized (mExtrasLock) {
+            mExtras = extras;
+            if (mExtras != null) {
+                b = new Bundle(mExtras);
+            }
+        }
+        onExtrasChanged(b);
     }
 }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index ef314f3..ff220f3 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -1239,6 +1239,7 @@
     private Conference mConference;
     private ConnectionService mConnectionService;
     private Bundle mExtras;
+    private final Object mExtrasLock = new Object();
 
     /**
      * Tracks the key set for the extras bundle provided on the last invocation of
@@ -1388,7 +1389,13 @@
      * @return The extras associated with this connection.
      */
     public final Bundle getExtras() {
-        return mExtras;
+        Bundle extras = null;
+        synchronized (mExtrasLock) {
+            if (mExtras != null) {
+                extras = new Bundle(mExtras);
+            }
+        }
+        return extras;
     }
 
     /**
@@ -1924,14 +1931,20 @@
         if (extras == null) {
             return;
         }
-
-        if (mExtras == null) {
-            mExtras = new Bundle();
+        // Creating a duplicate bundle so we don't have to synchronize on mExtrasLock while calling
+        // the listeners.
+        Bundle listenerExtras;
+        synchronized (mExtrasLock) {
+            if (mExtras == null) {
+                mExtras = new Bundle();
+            }
+            mExtras.putAll(extras);
+            listenerExtras = new Bundle(mExtras);
         }
-        mExtras.putAll(extras);
-
         for (Listener l : mListeners) {
-            l.onExtrasChanged(this, extras);
+            // Create a new clone of the extras for each listener so that they don't clobber
+            // each other
+            l.onExtrasChanged(this, new Bundle(listenerExtras));
         }
     }
 
@@ -1981,18 +1994,16 @@
      * @hide
      */
     public final void removeExtras(List<String> keys) {
-        if (mExtras != null) {
-            for (String key : keys) {
-                mExtras.remove(key);
-            }
-
-            if (mExtras.size() == 0) {
-                mExtras = null;
+        synchronized (mExtrasLock) {
+            if (mExtras != null) {
+                for (String key : keys) {
+                    mExtras.remove(key);
+                }
             }
         }
-
+        List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
         for (Listener l : mListeners) {
-            l.onExtrasRemoved(this, keys);
+            l.onExtrasRemoved(this, unmodifiableKeys);
         }
     }
 
@@ -2274,8 +2285,14 @@
      * @hide
      */
     final void handleExtrasChanged(Bundle extras) {
-        mExtras = extras;
-        onExtrasChanged(mExtras);
+        Bundle b = null;
+        synchronized (mExtrasLock) {
+            mExtras = extras;
+            if (mExtras != null) {
+                b = new Bundle(mExtras);
+            }
+        }
+        onExtrasChanged(b);
     }
 
     /**