Merge "Added tron metrics for shutdown time" into oc-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 5acef51..5471084 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30620,7 +30620,7 @@
     field public static final int N = 24; // 0x18
     field public static final int N_MR1 = 25; // 0x19
     field public static final int O = 26; // 0x1a
-    field public static final int O_MR1 = 10000; // 0x2710
+    field public static final int O_MR1 = 27; // 0x1b
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 2eaf544..b7b00f1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -16561,6 +16561,7 @@
     method public android.view.Display getDisplay(int);
     method public android.view.Display[] getDisplays();
     method public android.view.Display[] getDisplays(java.lang.String);
+    method public android.graphics.Point getStableDisplaySize();
     method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
     method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
     field public static final java.lang.String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
@@ -33333,7 +33334,7 @@
     field public static final int N = 24; // 0x18
     field public static final int N_MR1 = 25; // 0x19
     field public static final int O = 26; // 0x1a
-    field public static final int O_MR1 = 10000; // 0x2710
+    field public static final int O_MR1 = 27; // 0x1b
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index 4c6a11b..1708780 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -30764,7 +30764,7 @@
     field public static final int N = 24; // 0x18
     field public static final int N_MR1 = 25; // 0x19
     field public static final int O = 26; // 0x1a
-    field public static final int O_MR1 = 10000; // 0x2710
+    field public static final int O_MR1 = 27; // 0x1b
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 107dc86..bf2e45c 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -101,16 +101,15 @@
 
     struct uhid_event ev;
     memset(&ev, 0, sizeof(ev));
-    ev.type = UHID_CREATE2;
-    strncpy((char*)ev.u.create2.name, name, UHID_MAX_NAME_LENGTH);
-    memcpy(&ev.u.create2.rd_data, descriptor.get(),
-            descriptorSize * sizeof(ev.u.create2.rd_data[0]));
-    ev.u.create2.rd_size = descriptorSize;
-    ev.u.create2.bus = BUS_BLUETOOTH;
-    ev.u.create2.vendor = vid;
-    ev.u.create2.product = pid;
-    ev.u.create2.version = 0;
-    ev.u.create2.country = 0;
+    ev.type = UHID_CREATE;
+    strncpy((char*)ev.u.create.name, name, UHID_MAX_NAME_LENGTH);
+    ev.u.create.rd_data = descriptor.get();
+    ev.u.create.rd_size = descriptorSize;
+    ev.u.create.bus = BUS_BLUETOOTH;
+    ev.u.create.vendor = vid;
+    ev.u.create.product = pid;
+    ev.u.create.version = 0;
+    ev.u.create.country = 0;
 
     errno = 0;
     ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev)));
@@ -159,9 +158,9 @@
 void Device::sendReport(uint8_t* report, size_t reportSize) {
     struct uhid_event ev;
     memset(&ev, 0, sizeof(ev));
-    ev.type = UHID_INPUT2;
-    ev.u.input2.size = reportSize;
-    memcpy(&ev.u.input2.data, report, reportSize);
+    ev.type = UHID_INPUT;
+    ev.u.input.size = reportSize;
+    memcpy(&ev.u.input.data, report, reportSize);
     ssize_t ret = TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
     if (ret < 0 || ret != sizeof(ev)) {
         LOGE("Failed to send hid event: %s", strerror(errno));
diff --git a/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java b/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java
index b9fedd3..6a4a4be 100644
--- a/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java
+++ b/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java
@@ -34,6 +34,8 @@
             "       locksettings set-password [--old OLD_CREDENTIAL] NEW_PASSWORD\n" +
             "       locksettings clear [--old OLD_CREDENTIAL]\n" +
             "       locksettings verify [--old OLD_CREDENTIAL]\n" +
+            "       locksettings set-disabled DISABLED\n" +
+            "       locksettings get-disabled\n" +
             "\n" +
             "flags: \n" +
             "       --user USER_ID: specify the user, default value is current user\n" +
@@ -50,7 +52,11 @@
             "\n" +
             "locksettings clear: clears the unlock credential\n" +
             "\n" +
-            "locksettings verify: verifies the credential and unlocks the user\n";
+            "locksettings verify: verifies the credential and unlocks the user\n" +
+            "\n" +
+            "locksettings set-disabled: sets whether the lock screen should be disabled\n" +
+            "\n" +
+            "locksettings get-disabled: retrieves whether the lock screen is disabled\n";
 
     public static void main(String[] args) {
         (new LockSettingsCmd()).run(args);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 757795e..72d5ede 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7033,6 +7033,7 @@
     }
 
     final void performRestart() {
+        mCanEnterPictureInPicture = true;
         mFragments.noteStateNotSaved();
 
         if (mToken != null && mParent == null) {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 6dead3e..0bffc9e 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -195,6 +195,14 @@
     private static final String KEY_DOCK_CREATE_MODE = "android:activity.dockCreateMode";
 
     /**
+     * Determines whether to disallow the outgoing activity from entering picture-in-picture as the
+     * result of a new activity being launched.
+     * @hide
+     */
+    private static final String KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING =
+            "android:activity.disallowEnterPictureInPictureWhileLaunching";
+
+    /**
      * For Activity transitions, the calling Activity's TransitionListener used to
      * notify the called Activity when the shared element and the exit transitions
      * complete.
@@ -267,6 +275,7 @@
     private int mLaunchStackId = INVALID_STACK_ID;
     private int mLaunchTaskId = -1;
     private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+    private boolean mDisallowEnterPictureInPictureWhileLaunching;
     private boolean mTaskOverlay;
     private boolean mTaskOverlayCanResume;
     private AppTransitionAnimationSpec mAnimSpecs[];
@@ -856,6 +865,8 @@
         mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false);
         mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false);
         mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
+        mDisallowEnterPictureInPictureWhileLaunching = opts.getBoolean(
+                KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING, false);
         if (opts.containsKey(KEY_ANIM_SPECS)) {
             Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS);
             mAnimSpecs = new AppTransitionAnimationSpec[specs.length];
@@ -1121,6 +1132,16 @@
         mDockCreateMode = dockCreateMode;
     }
 
+    /** @hide */
+    public void setDisallowEnterPictureInPictureWhileLaunching(boolean disallow) {
+        mDisallowEnterPictureInPictureWhileLaunching = disallow;
+    }
+
+    /** @hide */
+    public boolean disallowEnterPictureInPictureWhileLaunching() {
+        return mDisallowEnterPictureInPictureWhileLaunching;
+    }
+
     /**
      * Update the current values in this ActivityOptions from those supplied
      * in <var>otherOptions</var>.  Any values
@@ -1275,6 +1296,8 @@
         b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
         b.putBoolean(KEY_TASK_OVERLAY_CAN_RESUME, mTaskOverlayCanResume);
         b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode);
+        b.putBoolean(KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING,
+                mDisallowEnterPictureInPictureWhileLaunching);
         if (mAnimSpecs != null) {
             b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
         }
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index b40c96c..363e20a 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -1,13 +1,18 @@
 package android.app;
 
-
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.content.ComponentName;
 import android.content.Context;
+import android.os.Handler;
 import android.os.RemoteException;
+import android.service.vr.IPersistentVrStateCallbacks;
 import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
+import android.util.ArrayMap;
+
+import java.util.Map;
 
 /**
  * Used to control aspects of a devices Virtual Reality (VR) capabilities.
@@ -16,7 +21,33 @@
 @SystemApi
 @SystemService(Context.VR_SERVICE)
 public class VrManager {
+
+    private static class CallbackEntry {
+        final IVrStateCallbacks mStateCallback = new IVrStateCallbacks.Stub() {
+            @Override
+            public void onVrStateChanged(boolean enabled) {
+                mHandler.post(() -> mCallback.onVrStateChanged(enabled));
+            }
+
+        };
+        final IPersistentVrStateCallbacks mPersistentStateCallback =
+                new IPersistentVrStateCallbacks.Stub() {
+            @Override
+            public void onPersistentVrStateChanged(boolean enabled) {
+                mHandler.post(() -> mCallback.onPersistentVrStateChanged(enabled));
+            }
+        };
+        final VrStateCallback mCallback;
+        final Handler mHandler;
+
+        CallbackEntry(VrStateCallback callback, Handler handler) {
+            mCallback = callback;
+            mHandler = handler;
+        }
+    }
+
     private final IVrManager mService;
+    private Map<VrStateCallback, CallbackEntry> mCallbackMap = new ArrayMap<>();
 
     /**
      * {@hide}
@@ -26,6 +57,84 @@
     }
 
     /**
+     * Registers a callback to be notified of changes to the VR Mode state.
+     *
+     * @param callback The callback to register.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
+    public void registerVrStateCallback(VrStateCallback callback, @NonNull Handler handler) {
+        if (callback == null || mCallbackMap.containsKey(callback)) {
+            return;
+        }
+
+        CallbackEntry entry = new CallbackEntry(callback, handler);
+        mCallbackMap.put(callback, entry);
+        try {
+            mService.registerListener(entry.mStateCallback);
+            mService.registerPersistentVrStateListener(entry.mPersistentStateCallback);
+        } catch (RemoteException e) {
+            try {
+                unregisterVrStateCallback(callback);
+            } catch (Exception ignore) {
+                e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Deregisters VR State callbacks.
+     *
+     * @param callback The callback to deregister.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
+    public void unregisterVrStateCallback(VrStateCallback callback) {
+        CallbackEntry entry = mCallbackMap.remove(callback);
+        if (entry != null) {
+            try {
+                mService.unregisterListener(entry.mStateCallback);
+            } catch (RemoteException ignore) {
+                // Dont rethrow exceptions from requests to unregister.
+            }
+
+            try {
+                mService.unregisterPersistentVrStateListener(entry.mPersistentStateCallback);
+            } catch (RemoteException ignore) {
+                // Dont rethrow exceptions from requests to unregister.
+            }
+        }
+    }
+
+    /**
+     * Returns the current VrMode state.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_VR_STATE)
+    public boolean getVrModeEnabled() {
+        try {
+            return mService.getVrModeState();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    /**
+     * Returns the current VrMode state.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_VR_STATE)
+    public boolean getPersistentVrModeEnabled() {
+        try {
+            return mService.getPersistentVrModeEnabled();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    /**
      * Sets the persistent VR mode state of a device. When a device is in persistent VR mode it will
      * remain in VR mode even if the foreground does not specify Vr mode being enabled. Mainly used
      * by VR viewers to indicate that a device is placed in a VR viewer.
diff --git a/core/java/android/app/VrStateCallback.java b/core/java/android/app/VrStateCallback.java
new file mode 100644
index 0000000..742faa06
--- /dev/null
+++ b/core/java/android/app/VrStateCallback.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 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 android.app;
+
+/**
+ * Listens to VR Mode state changes. Use with methods in {@link VrManager}.
+ *
+ * @hide
+ */
+public abstract class VrStateCallback {
+
+    /**
+     * Callback triggered when there is a change to Persistent VR State.
+     *
+     * @param enabled True when VR State is in persistent mode, false otherwise.
+     */
+    public void onPersistentVrStateChanged(boolean enabled) {}
+
+    /**
+     * Callback triggered when there is a change to Vr State.
+     *
+     * @param enabled True when VR State is in VR mode, false otherwise.
+     */
+    public void onVrStateChanged(boolean enabled) {}
+}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 3ade2e4..6fbacaf 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -18,8 +18,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.graphics.Point;
 import android.media.projection.MediaProjection;
 import android.os.Handler;
 import android.util.SparseArray;
@@ -586,6 +588,20 @@
     }
 
     /**
+     * Gets the stable device display size, in pixels.
+     *
+     * This should really only be used for things like server-side filtering of available
+     * applications. Most applications don't need the level of stability guaranteed by this and
+     * should instead query either the size of the display they're currently running on or the
+     * size of the default display.
+     * @hide
+     */
+    @SystemApi
+    public Point getStableDisplaySize() {
+        return mGlobal.getStableDisplaySize();
+    }
+
+    /**
      * Listens for changes in available display devices.
      */
     public interface DisplayListener {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 0b998e5..a8a4eb6 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.media.projection.IMediaProjection;
 import android.media.projection.MediaProjection;
@@ -444,6 +445,17 @@
         }
     }
 
+    /**
+     * Gets the stable device display size, in pixels.
+     */
+    public Point getStableDisplaySize() {
+        try {
+            return mDm.getStableDisplaySize();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
         @Override
         public void onDisplayEvent(int displayId, int event) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 7ca4dc1..5053884 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.display;
 
+import android.graphics.Point;
 import android.hardware.display.IDisplayManagerCallback;
 import android.hardware.display.IVirtualDisplayCallback;
 import android.hardware.display.WifiDisplay;
@@ -77,4 +78,7 @@
 
     // No permissions required but must be same Uid as the creator.
     void releaseVirtualDisplay(in IVirtualDisplayCallback token);
+
+    // Get a stable metric for the device's display size. No permissions required.
+    Point getStableDisplaySize();
 }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 7852125..0627998 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -773,7 +773,7 @@
         /**
          * O MR1.
          */
-        public static final int O_MR1 = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version.
+        public static final int O_MR1 = 27;
     }
 
     /** The type of build, like "user" or "eng". */
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 2755e8e..1dcaef4 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -306,7 +306,7 @@
  * <pre class="prettyprint">
  * private static String getCanonicalDomain(String domain) {
  *   InternetDomainName idn = InternetDomainName.from(domain);
- *   while (!idn.isTopPrivateDomain() && idn != null) {
+ *   while (idn != null && !idn.isTopPrivateDomain()) {
  *     idn = idn.parent();
  *   }
  *   return idn == null ? null : idn.toString();
@@ -314,8 +314,9 @@
  * </pre>
  *
  * <p>If the association between the web domain and app package cannot be verified through the steps
- * above, the service can still autofill the app, but it should warn the user about the potential
- * data leakage first, and askfor the user to confirm. For example, the service could:
+ * above, but the service thinks that it is appropriate to fill persisted credentials that are
+ * stored for the web domain, the service should warn the user about the potential data
+ * leakage first, and ask for the user to confirm. For example, the service could:
  *
  * <ol>
  *   <li>Create a dataset that requires
@@ -324,7 +325,7 @@
  *   <li>Include the web domain in the custom presentation for the
  *       {@link Dataset.Builder#setValue(AutofillId, AutofillValue, android.widget.RemoteViews)
  *       dataset value}.
- *   <li>When the user select that dataset, show a disclaimer dialog explaining that the app is
+ *   <li>When the user selects that dataset, show a disclaimer dialog explaining that the app is
  *       requesting credentials for a web domain, but the service could not verify if the app owns
  *       that domain. If the user agrees, then the service can unlock the dataset.
  *   <li>Similarly, when adding a {@link SaveInfo} object for the request, the service should
@@ -333,7 +334,7 @@
  *
  * <p>This same procedure could also be used when the autofillable data is contained inside an
  * {@code IFRAME}, in which case the WebView generates a new autofill context when a node inside
- * the {@code IFRAME} is focused, which the root node containing the {@code IFRAME}'s {@code src}
+ * the {@code IFRAME} is focused, with the root node containing the {@code IFRAME}'s {@code src}
  * attribute on {@link android.app.assist.AssistStructure.ViewNode#getWebDomain()}. A typical and
  * legitimate use case for this scenario is a financial app that allows the user
  * to login on different bank accounts. For example, a financial app {@code my_financial_app} could
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
index 9b37a65..c38fab1 100644
--- a/core/java/android/service/vr/IVrManager.aidl
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -59,6 +59,13 @@
     boolean getVrModeState();
 
     /**
+     * Returns the current Persistent VR mode state.
+     *
+     * @return {@code true} if Persistent VR mode is enabled.
+     */
+    boolean getPersistentVrModeEnabled();
+
+    /**
      * Sets the persistent VR mode state of a device. When a device is in persistent VR mode it will
      * remain in VR mode even if the foreground does not specify VR mode being enabled. Mainly used
      * by VR viewers to indicate that a device is placed in a VR viewer.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8a9b14e..ec8ffd7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7450,7 +7450,7 @@
      *       {@link ViewStructure#setAutofillOptions(CharSequence[])}.
      * </ul>
      *
-     * <p><b>NOTE:</b> the {@code left} and {@code top} values set in
+     * <p><b>Note:</b> The {@code left} and {@code top} values set in
      * {@link ViewStructure#setDimens(int, int, int, int, int, int)} must be relative to the next
      * {@link ViewGroup#isImportantForAutofill()} predecessor view included in the structure.
      *
@@ -7688,6 +7688,10 @@
      * {@link AutofillManager#notifyValueChanged(View)} must happen <b>after</b> the value was
      * changed to the autofilled value. If not, the view will not be considered autofilled.
      *
+     * <p><b>Note:</b> After this method is called, the value returned by
+     * {@link #getAutofillValue()} must be equal to the {@code value} passed to it, otherwise the
+     * view will not be highlighted as autofilled.
+     *
      * @param value value to be autofilled.
      */
     public void autofill(@SuppressWarnings("unused") AutofillValue value) {
@@ -7711,7 +7715,7 @@
      * <b>after</b> the value was changed to the autofilled value. If not, the child will not be
      * considered autofilled.
      *
-     * <p><b>NOTE:</b> to indicate that a virtual view was autofilled,
+     * <p><b>Note:</b> To indicate that a virtual view was autofilled,
      * <code>?android:attr/autofilledHighlight</code> should be drawn over it until the data
      * changes.
      *
@@ -7780,8 +7784,8 @@
     /**
      * Gets the {@link View}'s current autofill value.
      *
-     * <p>By default returns {@code null}, but views should override it to properly support the
-     * Autofill Framework.
+     * <p>By default returns {@code null}, but subclasses should override it and return an
+     * appropriate value to properly support the Autofill Framework.
      *
      * @see #onProvideAutofillStructure(ViewStructure, int)
      * @see #autofill(AutofillValue)
@@ -7833,7 +7837,7 @@
      *       be {@link #IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS}.
      * </ol>
      *
-     * <p><b>NOTE:</strong> setting the mode as does {@link #IMPORTANT_FOR_AUTOFILL_NO} or
+     * <p><b>Note:</b> Setting the mode as {@link #IMPORTANT_FOR_AUTOFILL_NO} or
      * {@link #IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS} does not guarantee the view (and its
      * children) will be always be considered not important; for example, when the user explicitly
      * makes an autofill request, all views are considered important. See
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f978b57..bab0306aa 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -112,6 +112,13 @@
      */
     void showGlobalActionsMenu();
 
+    /**
+     * Set whether the top app currently hides the statusbar.
+     *
+     * @param hidesStatusBar whether it is being hidden
+     */
+    void setTopAppHidesStatusBar(boolean hidesStatusBar);
+
     void addQsTile(in ComponentName tile);
     void remQsTile(in ComponentName tile);
     void clickQsTile(in ComponentName tile);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 86c0b43..89bbec2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3512,6 +3512,11 @@
     <permission android:name="android.permission.ACCESS_VR_MANAGER"
             android:protectionLevel="signature" />
 
+    <!-- Required to access VR-Mode state and state change events via {android.app.VrStateCallback}
+         @hide -->
+    <permission android:name="android.permission.ACCESS_VR_STATE"
+        android:protectionLevel="signature|preinstalled" />
+
     <!-- Allows an application to whitelist tasks during lock task mode
          @hide <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ca4b355..172cae9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3044,4 +3044,10 @@
 
     <!-- Allow SystemUI to show the shutdown dialog -->
     <bool name="config_showSysuiShutdown">true</bool>
+
+    <!-- The stable device width and height in pixels. If these aren't set to a positive number
+         then the device will use the width and height of the default display the first time it's
+         booted.  -->
+    <integer name="config_stableDeviceDisplayWidth">-1</integer>
+    <integer name="config_stableDeviceDisplayHeight">-1</integer>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 04cfe48..4cfc1ac 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3069,4 +3069,7 @@
   <java-symbol type="layout" name="shutdown_dialog" />
   <java-symbol type="dimen" name="chooser_service_spacing" />
   <java-symbol type="bool" name="config_showSysuiShutdown" />
+
+  <java-symbol type="integer" name="config_stableDeviceDisplayWidth" />
+  <java-symbol type="integer" name="config_stableDeviceDisplayHeight" />
 </resources>
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 05be088..dff31d4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -16,12 +16,12 @@
 
 package android.media;
 
-import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.app.NotificationManager;
@@ -2364,8 +2364,8 @@
      *      usecases such as voice memo recording, or speech recognition.
      *      Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
      *      as the playback of a song or a video.
-     * @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK}
-     *     and {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}.
+     * @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK},
+     *     {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS} and {@link #AUDIOFOCUS_FLAG_LOCK}.
      *     <br>Use 0 when not using any flags for the request, which behaves like
      *     {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, where either audio
      *     focus is granted immediately, or the grant request fails because the system is in a
@@ -2377,6 +2377,7 @@
      * @throws IllegalArgumentException
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public int requestAudioFocus(OnAudioFocusChangeListener l,
             @NonNull AudioAttributes requestAttributes,
             int durationHint,
@@ -2416,6 +2417,10 @@
      * @deprecated use {@link #requestAudioFocus(AudioFocusRequest, AudioPolicy)}
      */
     @SystemApi
+    @RequiresPermission(anyOf= {
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING
+    })
     public int requestAudioFocus(OnAudioFocusChangeListener l,
             @NonNull AudioAttributes requestAttributes,
             int durationHint,
@@ -2474,6 +2479,7 @@
      * @throws IllegalArgumentException when trying to lock focus without an AudioPolicy
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public int requestAudioFocus(@NonNull AudioFocusRequest afr, @Nullable AudioPolicy ap) {
         if (afr == null) {
             throw new NullPointerException("Illegal null AudioFocusRequest");
@@ -2571,6 +2577,7 @@
      * @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} are null.
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public int dispatchAudioFocusChange(@NonNull AudioFocusInfo afi, int focusChange,
             @NonNull AudioPolicy ap) {
         if (afi == null) {
@@ -2622,6 +2629,8 @@
      * @deprecated use {@link #abandonAudioFocusRequest(AudioFocusRequest)}
      */
     @SystemApi
+    @SuppressLint("Doclava125") // no permission enforcement, but only "undoes" what would have been
+                                // done by a matching requestAudioFocus
     public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
         int status = AUDIOFOCUS_REQUEST_FAILED;
         unregisterAudioFocusRequest(l);
@@ -3833,6 +3842,7 @@
      * @hide
      */
     @SystemApi
+    @SuppressLint("Doclava125") // FIXME is this still used?
     public boolean isHdmiSystemAudioSupported() {
         try {
             return getService().isHdmiSystemAudioSupported();
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 3322839..1e35cbc 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -55,9 +55,17 @@
     <dimen name="battery_height">14.5dp</dimen>
     <dimen name="battery_width">9.5dp</dimen>
 
+    <dimen name="bt_battery_padding">2dp</dimen>
+
     <!-- Margin on the right side of the system icon group on Keyguard. -->
     <fraction name="battery_button_height_fraction">10.5%</fraction>
 
+    <!-- Ratio between height of button part and height of total -->
+    <fraction name="bt_battery_button_height_fraction">7.5%</fraction>
+
+    <!-- Ratio between width and height -->
+    <fraction name="bt_battery_ratio_fraction">45%</fraction>
+
     <!-- Fraction value to smooth the edges of the battery icon. The path will be inset by this
          fraction of a pixel.-->
     <fraction name="battery_subpixel_smoothing_left">0%</fraction>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7b51725..bd884a3 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -484,6 +484,8 @@
     <string name="mobile_data_always_on">Mobile data always active</string>
     <!-- Setting Checkbox title whether to enable hardware acceleration for tethering. [CHAR LIMIT=80] -->
     <string name="tethering_hardware_offload">Tethering hardware acceleration</string>
+    <!-- Setting Checkbox title for showing Bluetooth devices without names -->
+    <string name="bluetooth_show_devices_without_names">Show Bluetooth devices without names</string>
     <!-- Setting Checkbox title for disabling Bluetooth absolute volume -->
     <string name="bluetooth_disable_absolute_volume">Disable absolute volume</string>
     <!-- Setting Checkbox title for enabling Bluetooth inband ringing -->
@@ -568,12 +570,13 @@
     <string name="verify_apps_over_usb_title">Verify apps over USB</string>
     <!-- Summary of checkbox setting to perform package verification on apps installed over USB/ADT/ADB [CHAR LIMIT=NONE] -->
     <string name="verify_apps_over_usb_summary">Check apps installed via ADB/ADT for harmful behavior.</string>
+    <!-- Summary of checkbox for showing Bluetooth devices without names -->
+    <string name="bluetooth_show_devices_without_names_summary">Bluetooth devices without names (MAC addresses only) will be displayed</string>
     <!-- Summary of checkbox for disabling Bluetooth absolute volume -->
     <string name="bluetooth_disable_absolute_volume_summary">Disables the Bluetooth absolute volume feature in case of volume issues with remote devices such as unacceptably loud volume or lack of control.</string>
     <!-- Summary of checkbox for enabling Bluetooth inband ringing -->
     <string name="bluetooth_enable_inband_ringing_summary">Allow ringtones on the phone to be played on Bluetooth headsets</string>
 
-
     <!-- Title of checkbox setting that enables the terminal app. [CHAR LIMIT=32] -->
     <string name="enable_terminal_title">Local terminal</string>
     <!-- Summary of checkbox setting that enables the terminal app. [CHAR LIMIT=64] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
index 426dc7c..d1f91d9 100755
--- a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
@@ -50,6 +50,7 @@
     protected final Paint mTextPaint;
     protected final Paint mBoltPaint;
     protected final Paint mPlusPaint;
+    protected float mButtonHeightFraction;
 
     private int mLevel = -1;
     private boolean mCharging;
@@ -66,7 +67,6 @@
     private final int mIntrinsicWidth;
     private final int mIntrinsicHeight;
 
-    private float mButtonHeightFraction;
     private float mSubpixelSmoothingLeft;
     private float mSubpixelSmoothingRight;
     private float mTextHeight, mWarningTextHeight;
@@ -298,7 +298,7 @@
 
         float drawFrac = (float) level / 100f;
         final int height = mHeight;
-        final int width = (int) (ASPECT_RATIO * mHeight);
+        final int width = (int) (getAspectRatio() * mHeight);
         final int px = (mWidth - width) / 2;
         final int buttonHeight = Math.round(height * mButtonHeightFraction);
 
@@ -329,7 +329,7 @@
 
         // define the battery shape
         mShapePath.reset();
-        final float radius = RADIUS_RATIO * (mFrame.height() + buttonHeight);
+        final float radius = getRadiusRatio() * (mFrame.height() + buttonHeight);
         mShapePath.setFillType(FillType.WINDING);
         mShapePath.addRoundRect(mFrame, radius, radius, Direction.CW);
         mShapePath.addRect(mButtonFrame, Direction.CW);
@@ -469,4 +469,12 @@
     public int getCriticalLevel() {
         return mCriticalLevel;
     }
+
+    protected float getAspectRatio() {
+        return ASPECT_RATIO;
+    }
+
+    protected float getRadiusRatio() {
+        return RADIUS_RATIO;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java b/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
new file mode 100644
index 0000000..61790b9
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 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.settingslib.graph;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.support.annotation.VisibleForTesting;
+import android.view.Gravity;
+import android.view.View;
+
+import com.android.settingslib.R;
+import com.android.settingslib.Utils;
+
+/**
+ * LayerDrawable contains the bluetooth device icon and battery gauge icon
+ */
+public class BluetoothDeviceLayerDrawable extends LayerDrawable {
+
+    private BluetoothDeviceLayerDrawableState mState;
+
+    private BluetoothDeviceLayerDrawable(@NonNull Drawable[] layers) {
+        super(layers);
+    }
+
+    /**
+     * Create the {@link LayerDrawable} that contains bluetooth device icon and battery icon.
+     * This is a vertical layout drawable while bluetooth icon at top and battery icon at bottom.
+     *
+     * @param context      used to get the spec for icon
+     * @param resId        represents the bluetooth device drawable
+     * @param batteryLevel the battery level for bluetooth device
+     */
+    public static BluetoothDeviceLayerDrawable createLayerDrawable(Context context, int resId,
+            int batteryLevel) {
+        final Drawable deviceDrawable = context.getDrawable(resId);
+
+        final BatteryMeterDrawable batteryDrawable = new BatteryMeterDrawable(context,
+                R.color.meter_background_color, batteryLevel);
+        final int pad = context.getResources()
+                .getDimensionPixelSize(R.dimen.bt_battery_padding);
+        batteryDrawable.setPadding(0, pad, 0, pad);
+
+        final BluetoothDeviceLayerDrawable drawable = new BluetoothDeviceLayerDrawable(
+                new Drawable[]{deviceDrawable,
+                        rotateDrawable(context.getResources(), batteryDrawable)});
+        // Set the bluetooth icon at the top
+        drawable.setLayerGravity(0 /* index of deviceDrawable */, Gravity.TOP);
+        // Set battery icon right below the bluetooth icon
+        drawable.setLayerInset(1 /* index of batteryDrawable */, 0,
+                deviceDrawable.getIntrinsicHeight(), 0, 0);
+
+        drawable.setConstantState(context, resId, batteryLevel);
+
+        return drawable;
+    }
+
+    /**
+     * Rotate the {@code drawable} by 90 degree clockwise and return rotated {@link Drawable}
+     */
+    private static Drawable rotateDrawable(Resources res, Drawable drawable) {
+        // Get the bitmap from drawable
+        final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+                drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(bitmap);
+        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        drawable.draw(canvas);
+
+        // Create rotate matrix
+        final Matrix matrix = new Matrix();
+        matrix.postRotate(
+                res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR
+                        ? 90 : 270);
+
+        // Create new bitmap with rotate matrix
+        final Bitmap rotateBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
+                bitmap.getHeight(), matrix, true);
+        bitmap.recycle();
+
+        return new BitmapDrawable(res, rotateBitmap);
+    }
+
+    public void setConstantState(Context context, int resId, int batteryLevel) {
+        mState = new BluetoothDeviceLayerDrawableState(context, resId, batteryLevel);
+    }
+
+    @Override
+    public ConstantState getConstantState() {
+        return mState;
+    }
+
+    /**
+     * Battery gauge icon with new spec.
+     */
+    @VisibleForTesting
+    static class BatteryMeterDrawable extends BatteryMeterDrawableBase {
+        private final float mAspectRatio;
+
+        public BatteryMeterDrawable(Context context, int frameColor, int batteryLevel) {
+            super(context, frameColor);
+            final Resources resources = context.getResources();
+            mButtonHeightFraction = resources.getFraction(
+                    R.fraction.bt_battery_button_height_fraction, 1, 1);
+            mAspectRatio = resources.getFraction(R.fraction.bt_battery_ratio_fraction, 1, 1);
+
+            final int tintColor = Utils.getColorAttr(context, android.R.attr.colorControlNormal);
+            setColorFilter(new PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN));
+            setBatteryLevel(batteryLevel);
+        }
+
+        @Override
+        protected float getAspectRatio() {
+            return mAspectRatio;
+        }
+
+        @Override
+        protected float getRadiusRatio() {
+            // Remove the round edge
+            return 0;
+        }
+    }
+
+    /**
+     * {@link ConstantState} to restore the {@link BluetoothDeviceLayerDrawable}
+     */
+    private static class BluetoothDeviceLayerDrawableState extends ConstantState {
+        Context context;
+        int resId;
+        int batteryLevel;
+
+        public BluetoothDeviceLayerDrawableState(Context context, int resId,
+                int batteryLevel) {
+            this.context = context;
+            this.resId = resId;
+            this.batteryLevel = batteryLevel;
+        }
+
+        @Override
+        public Drawable newDrawable() {
+            return createLayerDrawable(context, resId, batteryLevel);
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return 0;
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
new file mode 100644
index 0000000..89855be
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 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.settingslib.graph;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.VectorDrawable;
+
+import com.android.settingslib.R;
+import com.android.settingslib.SettingLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.testutils.shadow.SettingsLibShadowResources;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+        shadows = SettingsLibShadowResources.class)
+public class BluetoothDeviceLayerDrawableTest {
+    private static final int RES_ID = R.drawable.ic_bt_cellphone;
+    private static final int BATTERY_LEVEL = 15;
+    private static final float TOLERANCE = 0.001f;
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+    }
+
+    @Test
+    public void testCreateLayerDrawable_configCorrect() {
+        BluetoothDeviceLayerDrawable drawable = BluetoothDeviceLayerDrawable.createLayerDrawable(
+                mContext, RES_ID, BATTERY_LEVEL);
+
+        assertThat(drawable.getDrawable(0)).isInstanceOf(VectorDrawable.class);
+        assertThat(drawable.getDrawable(1)).isInstanceOf(BitmapDrawable.class);
+        assertThat(drawable.getLayerInsetTop(1)).isEqualTo(
+                drawable.getDrawable(0).getIntrinsicHeight());
+    }
+
+    @Test
+    public void testBatteryMeterDrawable_configCorrect() {
+        BluetoothDeviceLayerDrawable.BatteryMeterDrawable batteryDrawable =
+                new BluetoothDeviceLayerDrawable.BatteryMeterDrawable(mContext,
+                        R.color.meter_background_color, BATTERY_LEVEL);
+
+        assertThat(batteryDrawable.getAspectRatio()).isWithin(TOLERANCE).of(0.45f);
+        assertThat(batteryDrawable.getRadiusRatio()).isWithin(TOLERANCE).of(0f);
+        assertThat(batteryDrawable.getBatteryLevel()).isEqualTo(BATTERY_LEVEL);
+    }
+
+    @Test
+    public void testConstantState_returnTwinBluetoothLayerDrawable() {
+        BluetoothDeviceLayerDrawable drawable = BluetoothDeviceLayerDrawable.createLayerDrawable(
+                mContext, RES_ID, BATTERY_LEVEL);
+
+        BluetoothDeviceLayerDrawable twinDrawable =
+                (BluetoothDeviceLayerDrawable) drawable.getConstantState().newDrawable();
+
+        assertThat(twinDrawable.getDrawable(0)).isEqualTo(drawable.getDrawable(0));
+        assertThat(twinDrawable.getDrawable(1)).isEqualTo(drawable.getDrawable(1));
+        assertThat(twinDrawable.getLayerInsetTop(1)).isEqualTo(
+                drawable.getLayerInsetTop(1));
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java
new file mode 100644
index 0000000..a376dcd
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 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.settingslib.testutils.shadow;
+
+import static org.robolectric.internal.Shadow.directlyOn;
+
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.support.annotation.ArrayRes;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.shadows.ShadowResources;
+
+/**
+ * Shadow Resources to handle resource references that Robolectric shadows cannot
+ * handle because they are too new or private.
+ */
+@Implements(Resources.class)
+public class SettingsLibShadowResources extends ShadowResources {
+
+    @RealObject
+    public Resources realResources;
+
+    @Implementation
+    public int[] getIntArray(@ArrayRes int id) throws NotFoundException {
+        // The Robolectric has resource mismatch for these values, so we need to stub it here
+        if (id == com.android.settingslib.R.array.batterymeter_bolt_points
+                || id == com.android.settingslib.R.array.batterymeter_plus_points) {
+            return new int[2];
+        }
+        return directlyOn(realResources, Resources.class).getIntArray(id);
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
index 1285ed8..56a3ee3 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
@@ -27,7 +27,7 @@
 
     public static final String ACTION = "com.android.systemui.action.PLUGIN_NAV_BUTTON";
 
-    public static final int VERSION = 3;
+    public static final int VERSION = 2;
 
     /**
      * Returns a view in the nav bar.  If the id is set "back", "home", "recent_apps", "menu",
@@ -46,6 +46,9 @@
 
         void setVertical(boolean vertical);
 
+        default void setCarMode(boolean carMode) {
+        }
+
         void setDarkIntensity(float intensity);
     }
 }
diff --git a/packages/SystemUI/res/layout/global_actions_item.xml b/packages/SystemUI/res/layout/global_actions_item.xml
index e3a488c..0d735e7 100644
--- a/packages/SystemUI/res/layout/global_actions_item.xml
+++ b/packages/SystemUI/res/layout/global_actions_item.xml
@@ -25,8 +25,8 @@
     android:minHeight="92dp"
     android:gravity="center"
     android:orientation="vertical"
-    android:paddingEnd="8dip"
-    android:paddingStart="8dip">
+    android:paddingEnd="0dip"
+    android:paddingStart="0dip">
 
     <ImageView
         android:id="@*android:id/icon"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index 4c7b48d..7225ba9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -60,9 +60,19 @@
     KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
         @Override
         public void onSimStateChanged(int subId, int slotId, State simState) {
-           if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
-           resetState();
-       };
+            if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+            switch(simState) {
+                // If the SIM is removed, then we must remove the keyguard. It will be put up
+                // again when the PUK locked SIM is re-entered.
+                case ABSENT: {
+                    KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
+                    mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+                    break;
+                }
+                default:
+                    resetState();
+            }
+        }
     };
 
     public KeyguardSimPinView(Context context) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index d8163ba..171cf23 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -62,9 +62,23 @@
     KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
         @Override
         public void onSimStateChanged(int subId, int slotId, State simState) {
-           if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
-           resetState();
-       };
+            if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+            switch(simState) {
+                // If the SIM is removed, then we must remove the keyguard. It will be put up
+                // again when the PUK locked SIM is re-entered.
+                case ABSENT:
+                // intentional fall-through
+                // If the SIM is unlocked via a key sequence through the emergency dialer, it will
+                // move into the READY state and the PUK lock keyguard should be removed.
+                case READY: {
+                    KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
+                    mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+                    break;
+                }
+                default:
+                    resetState();
+            }
+        }
     };
 
     public KeyguardSimPukView(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 8c1b736..4b37715 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -83,6 +83,7 @@
 
     private boolean mMenuRowIntercepting;
     private boolean mLongPressSent;
+    private LongPressListener mLongPressListener;
     private Runnable mWatchLongPress;
     private final long mLongPressTimeout;
 
@@ -114,6 +115,10 @@
         mFlingAnimationUtils = new FlingAnimationUtils(context, getMaxEscapeAnimDuration() / 1000f);
     }
 
+    public void setLongPressListener(LongPressListener listener) {
+        mLongPressListener = listener;
+    }
+
     public void setDensityScale(float densityScale) {
         mDensityScale = densityScale;
     }
@@ -252,7 +257,7 @@
         }
     }
 
-    public void cancelLongPress() {
+    public void removeLongPressCallback() {
         if (mWatchLongPress != null) {
             mHandler.removeCallbacks(mWatchLongPress);
             mWatchLongPress = null;
@@ -276,14 +281,6 @@
                 mVelocityTracker.clear();
                 mCurrView = mCallback.getChildAtPosition(ev);
 
-                // The SwipeHelper sends its own long-press, don't let the view send a dupe.
-                // Queue up a cancelLongPress on the view a few ms after we see a down event.
-                mHandler.post(() -> {
-                    if (mCurrView != null) {
-                        mCurrView.cancelLongPress();
-                    }
-                });
-
                 if (mCurrView != null) {
                     onDownUpdate(mCurrView, ev);
                     mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
@@ -291,26 +288,33 @@
                     mInitialTouchPos = getPos(ev);
                     mPerpendicularInitialTouchPos = getPerpendicularPos(ev);
                     mTranslation = getTranslation(mCurrView);
-                    if (mWatchLongPress == null) {
-                        mWatchLongPress = new Runnable() {
-                            @Override
-                            public void run() {
-                                if (mCurrView != null && !mLongPressSent) {
-                                    mLongPressSent = true;
-                                    mCurrView.getLocationOnScreen(mTmpPos);
-                                    final int x = (int) ev.getRawX() - mTmpPos[0];
-                                    final int y = (int) ev.getRawY() - mTmpPos[1];
-                                    if (mCurrView instanceof ExpandableNotificationRow) {
-                                        ExpandableNotificationRow currRow =
-                                                (ExpandableNotificationRow) mCurrView;
-                                        currRow.setLongPressPosition(x, y);
-                                        currRow.performLongClick(x, y);
+                    if (mLongPressListener != null) {
+                        if (mWatchLongPress == null) {
+                            mWatchLongPress = new Runnable() {
+                                @Override
+                                public void run() {
+                                    if (mCurrView != null && !mLongPressSent) {
+                                        mLongPressSent = true;
+                                        mCurrView.sendAccessibilityEvent(
+                                                AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+                                        mCurrView.getLocationOnScreen(mTmpPos);
+                                        final int x = (int) ev.getRawX() - mTmpPos[0];
+                                        final int y = (int) ev.getRawY() - mTmpPos[1];
+                                        MenuItem menuItem = null;
+                                        if (mCurrView instanceof ExpandableNotificationRow) {
+                                            menuItem = ((ExpandableNotificationRow) mCurrView)
+                                                    .getProvider().getLongpressMenuItem(mContext);
+                                        }
+                                        if (menuItem != null) {
+                                            mLongPressListener.onLongPress(mCurrView, x, y,
+                                                    menuItem);
+                                        }
                                     }
                                 }
-                            }
-                        };
+                            };
+                        }
+                        mHandler.postDelayed(mWatchLongPress, mLongPressTimeout);
                     }
-                    mHandler.postDelayed(mWatchLongPress, mLongPressTimeout);
                 }
                 break;
 
@@ -327,7 +331,7 @@
                         mDragging = true;
                         mInitialTouchPos = getPos(ev);
                         mTranslation = getTranslation(mCurrView);
-                        cancelLongPress();
+                        removeLongPressCallback();
                     }
                 }
                 break;
@@ -339,7 +343,7 @@
                 mCurrView = null;
                 mLongPressSent = false;
                 mMenuRowIntercepting = false;
-                cancelLongPress();
+                removeLongPressCallback();
                 if (captured) return true;
                 break;
         }
@@ -582,7 +586,7 @@
 
                 // We are not doing anything, make sure the long press callback
                 // is not still ticking like a bomb waiting to go off.
-                cancelLongPress();
+                removeLongPressCallback();
                 return false;
             }
         }
@@ -730,4 +734,15 @@
          */
         float getFalsingThresholdFactor();
     }
+
+    /**
+     * Equivalent to View.OnLongClickListener with coordinates
+     */
+    public interface LongPressListener {
+        /**
+         * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
+         * @return whether the longpress was handled
+         */
+        boolean onLongPress(View v, int x, int y, MenuItem item);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index bbf9eb1..6349275 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -81,6 +81,7 @@
     private static final int MSG_SHOW_GLOBAL_ACTIONS           = 34 << MSG_SHIFT;
     private static final int MSG_TOGGLE_PANEL                  = 35 << MSG_SHIFT;
     private static final int MSG_SHOW_SHUTDOWN_UI              = 36 << MSG_SHIFT;
+    private static final int MSG_SET_TOP_APP_HIDES_STATUS_BAR  = 37 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -132,6 +133,7 @@
         default void startAssist(Bundle args) { }
         default void onCameraLaunchGestureDetected(int source) { }
         default void showPictureInPictureMenu() { }
+        default void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) { }
 
         default void addQsTile(ComponentName tile) { }
         default void remQsTile(ComponentName tile) { }
@@ -441,6 +443,13 @@
     }
 
     @Override
+    public void setTopAppHidesStatusBar(boolean hidesStatusBar) {
+        mHandler.removeMessages(MSG_SET_TOP_APP_HIDES_STATUS_BAR);
+        mHandler.obtainMessage(MSG_SET_TOP_APP_HIDES_STATUS_BAR, hidesStatusBar ? 1 : 0, 0)
+                .sendToTarget();
+    }
+
+    @Override
     public void showShutdownUi(boolean isReboot, String reason) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_SHUTDOWN_UI);
@@ -640,6 +649,11 @@
                         mCallbacks.get(i).handleShowShutdownUi(msg.arg1 != 0, (String) msg.obj);
                     }
                     break;
+                case MSG_SET_TOP_APP_HIDES_STATUS_BAR:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).setTopAppHidesStatusBar(msg.arg1 != 0);
+                    }
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index b43626f..7bc1a39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -172,14 +172,6 @@
     private boolean mShowNoBackground;
     private ExpandableNotificationRow mNotificationParent;
     private OnExpandClickListener mOnExpandClickListener;
-
-    // Listener will be called when receiving a long click event.
-    // Use #setLongPressPosition to optionally assign positional data with the long press.
-    private LongPressListener mLongPressListener;
-    private boolean mLongPressPositionSet = false;
-    private int mLongPressX = 0;
-    private int mLongPressY = 0;
-
     private boolean mGroupExpansionChanging;
 
     /**
@@ -781,16 +773,6 @@
         mOnExpandClickListener = onExpandClickListener;
     }
 
-    public void setLongPressListener(LongPressListener longPressListener) {
-        mLongPressListener = longPressListener;
-    }
-
-    public void setLongPressPosition(int x, int y) {
-        mLongPressPositionSet = true;
-        mLongPressX = x;
-        mLongPressY = y;
-    }
-
     @Override
     public void setOnClickListener(@Nullable OnClickListener l) {
         super.setOnClickListener(l);
@@ -1339,25 +1321,6 @@
             mTranslateableViews.remove(mChildrenContainerStub);
             mTranslateableViews.remove(mGutsStub);
         }
-
-        setOnLongClickListener((View v) -> {
-            createMenu();
-            MenuItem menuItem = getProvider().getLongpressMenuItem(mContext);
-            if (mLongPressListener != null && menuItem != null) {
-                int x, y;
-                if (mLongPressPositionSet) {
-                    x = mLongPressX;
-                    y = mLongPressY;
-                } else {
-                    // No position assigned - use the center of the View
-                    x = getWidth() / 2;
-                    y = getHeight() / 2;
-                }
-                mLongPressPositionSet = false;
-                return mLongPressListener.onLongPress(this, x, y, menuItem);
-            }
-            return false;
-        });
     }
 
     public void resetTranslation() {
@@ -2341,15 +2304,4 @@
     protected void setChildrenContainer(NotificationChildrenContainer childrenContainer) {
         mChildrenContainer = childrenContainer;
     }
-
-    /**
-     * Equivalent to View.OnLongClickListener with coordinates
-     */
-    public interface LongPressListener {
-        /**
-         * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
-         * @return whether the longpress was handled
-         */
-        boolean onLongPress(View v, int x, int y, MenuItem item);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 2c50736..680f693 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -44,7 +44,6 @@
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
@@ -248,12 +247,11 @@
     }
 
     /**
-     * Returns the
-     * {@link com.android.systemui.statusbar.ExpandableNotificationRow.LongPressListener} that will
-     * be triggered when a notification card is long-pressed.
+     * Returns the {@link com.android.systemui.SwipeHelper.LongPressListener} that will be
+     * triggered when a notification card is long-pressed.
      */
     @Override
-    protected ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
+    protected SwipeHelper.LongPressListener getNotificationLongClicker() {
         // For the automative use case, we do not want to the user to be able to interact with
         // a notification other than a regular click. As a result, just return null for the
         // long click listener.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 4339ade..5c04058 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -179,7 +179,13 @@
     }
 
     private boolean shouldHideNotificationIcons() {
-        return !mStatusBar.isClosed() && mStatusBarComponent.hideStatusBarIconsWhenExpanded();
+        if (!mStatusBar.isClosed() && mStatusBarComponent.hideStatusBarIconsWhenExpanded()) {
+            return true;
+        }
+        if (mStatusBarComponent.hideStatusBarIconsForBouncer()) {
+            return true;
+        }
+        return false;
     }
 
     public void hideSystemIconArea(boolean animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 6cfa838..f058862 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -72,7 +72,6 @@
 import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
 import com.android.systemui.plugins.IntentButtonProvider.IntentButton.IconState;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
@@ -82,10 +81,8 @@
 import com.android.systemui.statusbar.policy.ExtensionController.Extension;
 import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.PreviewInflater;
-import com.android.systemui.tuner.LockscreenFragment;
 import com.android.systemui.tuner.LockscreenFragment.LockButtonFactory;
 import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
 
 /**
  * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
@@ -510,6 +507,7 @@
                     // force the crossfade animation if an orientation change
                     // happens to occur during the launch.
                     ActivityOptions o = ActivityOptions.makeBasic();
+                    o.setDisallowEnterPictureInPictureWhileLaunching(true);
                     o.setRotationAnimationHint(
                             WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
                     try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 9453035..078e818 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -32,6 +32,7 @@
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
+import android.os.PowerManager;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.MathUtils;
@@ -108,6 +109,7 @@
                     return object.mDarkAmount;
                 }
             };
+    private final PowerManager mPowerManager;
 
     private KeyguardAffordanceHelper mAffordanceHelper;
     private KeyguardUserSwitcher mKeyguardUserSwitcher;
@@ -242,6 +244,7 @@
         super(context, attrs);
         setWillNotDraw(!DEBUG);
         mFalsingManager = FalsingManager.getInstance(context);
+        mPowerManager = context.getSystemService(PowerManager.class);
     }
 
     public void setStatusBar(StatusBar bar) {
@@ -700,7 +703,7 @@
                     mInitialHeightOnTouch = mQsExpansionHeight;
                     mQsTracking = true;
                     mIntercepting = false;
-                    mNotificationStackScroller.cancelLongPress();
+                    mNotificationStackScroller.removeLongPressCallback();
                 }
                 break;
             case MotionEvent.ACTION_POINTER_UP:
@@ -736,7 +739,7 @@
                     mInitialTouchY = y;
                     mInitialTouchX = x;
                     mIntercepting = false;
-                    mNotificationStackScroller.cancelLongPress();
+                    mNotificationStackScroller.removeLongPressCallback();
                     return true;
                 }
                 break;
@@ -1974,6 +1977,11 @@
 
     @Override
     protected void startUnlockHintAnimation() {
+        if (mPowerManager.isPowerSaveMode()) {
+            onUnlockHintStarted();
+            onUnlockHintFinished();
+            return;
+        }
         super.startUnlockHintAnimation();
         startHighlightIconAnimation(getCenterIcon());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index b2c97b1..75db6e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -104,7 +104,6 @@
 import android.os.UserManager;
 import android.os.Vibrator;
 import android.provider.Settings;
-import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.service.vr.IVrManager;
@@ -143,7 +142,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.graphics.ColorUtils;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -167,6 +165,7 @@
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
+import com.android.systemui.SwipeHelper;
 import com.android.systemui.SystemUI;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.UiOffloadThread;
@@ -639,6 +638,12 @@
 
     // Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
     private int mLastLoggedStateFingerprint;
+    private boolean mTopHidesStatusBar;
+    private boolean mStatusBarWindowHidden;
+    private boolean mHideIconsForBouncer;
+    private boolean mIsOccluded;
+    private boolean mWereIconsJustHidden;
+    private boolean mBouncerWasShowingWhenHidden;
 
     public boolean isStartedGoingToSleep() {
         return mStartedGoingToSleep;
@@ -2674,7 +2679,8 @@
 
     @Override
     public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
-        startActivityDismissingKeyguard(intent, false, dismissShade, callback);
+        startActivityDismissingKeyguard(intent, false, dismissShade,
+                false /* disallowEnterPictureInPictureWhileLaunching */, callback);
     }
 
     public void setQsExpanded(boolean expanded) {
@@ -2825,6 +2831,7 @@
 
     public void setPanelExpanded(boolean isExpanded) {
         mPanelExpanded = isExpanded;
+        updateHideIconsForBouncer(false /* animate */);
         mStatusBarWindowManager.setPanelExpanded(isExpanded);
         mVisualStabilityManager.setPanelExpanded(isExpanded);
         if (isExpanded && getBarState() != StatusBarState.KEYGUARD) {
@@ -2890,6 +2897,40 @@
         return mAmbientIndicationContainer;
     }
 
+    public void setOccluded(boolean occluded) {
+        mIsOccluded = occluded;
+        updateHideIconsForBouncer(false /* animate */);
+    }
+
+    public boolean hideStatusBarIconsForBouncer() {
+        return mHideIconsForBouncer || mWereIconsJustHidden;
+    }
+
+    /**
+     * @param animate should the change of the icons be animated.
+     */
+    private void updateHideIconsForBouncer(boolean animate) {
+        boolean shouldHideIconsForBouncer = !mPanelExpanded && mTopHidesStatusBar && mIsOccluded
+                && (mBouncerShowing || mStatusBarWindowHidden);
+        if (mHideIconsForBouncer != shouldHideIconsForBouncer) {
+            mHideIconsForBouncer = shouldHideIconsForBouncer;
+            if (!shouldHideIconsForBouncer && mBouncerWasShowingWhenHidden) {
+                // We're delaying the showing, since most of the time the fullscreen app will
+                // hide the icons again and we don't want them to fade in and out immediately again.
+                mWereIconsJustHidden = true;
+                mHandler.postDelayed(() -> {
+                    mWereIconsJustHidden = false;
+                    recomputeDisableFlags(true);
+                }, 500);
+            } else {
+                recomputeDisableFlags(animate);
+            }
+        }
+        if (shouldHideIconsForBouncer) {
+            mBouncerWasShowingWhenHidden = mBouncerShowing;
+        }
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
@@ -3216,6 +3257,10 @@
                 mStatusBarView.collapsePanel(false /* animate */, false /* delayed */,
                         1.0f /* speedUpFactor */);
             }
+            if (mStatusBarView != null) {
+                mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN;
+                updateHideIconsForBouncer(false /* animate */);
+            }
         }
     }
 
@@ -3628,11 +3673,13 @@
 
     public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
             boolean dismissShade) {
-        startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, null /* callback */);
+        startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade,
+                false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */);
     }
 
     public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
-            final boolean dismissShade, final Callback callback) {
+            final boolean dismissShade, final boolean disallowEnterPictureInPictureWhileLaunching,
+            final Callback callback) {
         if (onlyProvisioned && !isDeviceProvisioned()) return;
 
         final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
@@ -3645,6 +3692,8 @@
                         Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                 int result = ActivityManager.START_CANCELED;
                 ActivityOptions options = new ActivityOptions(getActivityOptions());
+                options.setDisallowEnterPictureInPictureWhileLaunching(
+                        disallowEnterPictureInPictureWhileLaunching);
                 if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) {
                     // Normally an activity will set it's requested rotation
                     // animation on its window. However when launching an activity
@@ -4939,7 +4988,7 @@
 
     @Override
     public void onTouchSlopExceeded() {
-        mStackScroller.cancelLongPress();
+        mStackScroller.removeLongPressCallback();
         mStackScroller.checkSnoozeLeavebehind();
     }
 
@@ -5162,6 +5211,7 @@
     public void setBouncerShowing(boolean bouncerShowing) {
         mBouncerShowing = bouncerShowing;
         if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing);
+        updateHideIconsForBouncer(true /* animate */);
         recomputeDisableFlags(true /* animate */);
     }
 
@@ -5345,8 +5395,9 @@
         }
         vibrateForCameraGesture();
         if (!mStatusBarKeyguardViewManager.isShowing()) {
-            startActivity(KeyguardBottomAreaView.INSECURE_CAMERA_INTENT,
-                    true /* dismissShade */);
+            startActivityDismissingKeyguard(KeyguardBottomAreaView.INSECURE_CAMERA_INTENT,
+                    false /* onlyProvisioned */, true /* dismissShade */,
+                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */);
         } else {
             if (!mDeviceInteractive) {
                 // Avoid flickering of the scrim when we instant launch the camera and the bouncer
@@ -5580,7 +5631,7 @@
 
         @Override
         public void onDoubleTap(float screenX, float screenY) {
-            if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
+            if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null 
                 && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
                 mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2);
                 float viewX = screenX - mTmpInt2[0];
@@ -6426,15 +6477,14 @@
                 true /* removeControls */, x, y, true /* resetMenu */);
     }
 
-    protected ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
-        return new ExpandableNotificationRow.LongPressListener() {
+    protected SwipeHelper.LongPressListener getNotificationLongClicker() {
+        return new SwipeHelper.LongPressListener() {
             @Override
             public boolean onLongPress(View v, final int x, final int y,
                     MenuItem item) {
                 if (!(v instanceof ExpandableNotificationRow)) {
                     return false;
                 }
-
                 if (v.getWindowToken() == null) {
                     Log.e(TAG, "Trying to show notification guts, but not attached to window");
                     return false;
@@ -6449,7 +6499,7 @@
                     closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
                             true /* removeControls */, -1 /* x */, -1 /* y */,
                             true /* resetMenu */);
-                    return true;
+                    return false;
                 }
                 bindGuts(row, item);
                 NotificationGuts guts = row.getGuts();
@@ -6568,6 +6618,18 @@
         mHandler.obtainMessage(msg, deviceId, 0).sendToTarget();
     }
 
+    @Override
+    public void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) {
+        mTopHidesStatusBar = topAppHidesStatusBar;
+        if (!topAppHidesStatusBar && mWereIconsJustHidden) {
+            // Immediately update the icon hidden state, since that should only apply if we're
+            // staying fullscreen.
+            mWereIconsJustHidden = false;
+            recomputeDisableFlags(true);
+        }
+        updateHideIconsForBouncer(true /* animate */);
+    }
+
     protected void sendCloseSystemWindows(String reason) {
         try {
             ActivityManager.getService().closeSystemDialogs(reason);
@@ -6735,7 +6797,6 @@
         row.setOnExpandClickListener(this);
         row.setRemoteViewClickHandler(mOnClickHandler);
         row.setInflationCallback(this);
-        row.setLongPressListener(getNotificationLongClicker());
 
         // Get the app name.
         // Note that Notification.Builder#bindHeaderAppName has similar logic
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index b4b859c..bbce751 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -302,6 +302,7 @@
     }
 
     public void setOccluded(boolean occluded, boolean animate) {
+        mStatusBar.setOccluded(occluded);
         if (occluded && !mOccluded && mShowing) {
             if (mStatusBar.isInLaunchTransition()) {
                 mOccluded = 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 dcf7439..4e592db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -40,6 +40,7 @@
 import android.os.Handler;
 import android.service.notification.StatusBarNotification;
 import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
@@ -241,7 +242,7 @@
      * motion.
      */
     private int mMaxScrollAfterExpand;
-    private ExpandableNotificationRow.LongPressListener mLongPressListener;
+    private SwipeHelper.LongPressListener mLongPressListener;
 
     private NotificationMenuRowPlugin mCurrMenuRow;
     private View mTranslatingParentView;
@@ -411,6 +412,7 @@
         mExpandHelper.setEventSource(this);
         mExpandHelper.setScrollAdapter(this);
         mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
+        mSwipeHelper.setLongPressListener(mLongPressListener);
         mStackScrollAlgorithm = createStackScrollAlgorithm(context);
         initView(context);
         mFalsingManager = FalsingManager.getInstance(context);
@@ -884,7 +886,8 @@
         return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
     }
 
-    public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
+    public void setLongPressListener(SwipeHelper.LongPressListener listener) {
+        mSwipeHelper.setLongPressListener(listener);
         mLongPressListener = listener;
     }
 
@@ -1160,7 +1163,7 @@
         if (v instanceof ExpandableNotificationRow) {
             ((ExpandableNotificationRow) v).setUserLocked(userLocked);
         }
-        cancelLongPress();
+        removeLongPressCallback();
         requestDisallowInterceptTouchEvent(true);
     }
 
@@ -2566,7 +2569,7 @@
     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
         super.requestDisallowInterceptTouchEvent(disallowIntercept);
         if (disallowIntercept) {
-            cancelLongPress();
+            mSwipeHelper.removeLongPressCallback();
         }
     }
 
@@ -3287,7 +3290,7 @@
         mIsBeingDragged = isDragged;
         if (isDragged) {
             requestDisallowInterceptTouchEvent(true);
-            cancelLongPress();
+            removeLongPressCallback();
         }
     }
 
@@ -3295,7 +3298,7 @@
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         super.onWindowFocusChanged(hasWindowFocus);
         if (!hasWindowFocus) {
-            cancelLongPress();
+            removeLongPressCallback();
         }
     }
 
@@ -3309,7 +3312,7 @@
 
     @Override
     public void requestDisallowLongPress() {
-        cancelLongPress();
+        removeLongPressCallback();
     }
 
     @Override
@@ -3317,8 +3320,8 @@
         mDisallowDismissInThisMotion = true;
     }
 
-    public void cancelLongPress() {
-        mSwipeHelper.cancelLongPress();
+    public void removeLongPressCallback() {
+        mSwipeHelper.removeLongPressCallback();
     }
 
     @Override
@@ -3543,6 +3546,7 @@
      * See {@link AmbientState#setDimmed}.
      */
     public void setDimmed(boolean dimmed, boolean animate) {
+        dimmed &= onKeyguard();
         mAmbientState.setDimmed(dimmed);
         if (animate && mAnimationsEnabled) {
             mDimmedNeedsAnimation = true;
@@ -3554,6 +3558,11 @@
         requestChildrenUpdate();
     }
 
+    @VisibleForTesting
+    boolean isDimmed() {
+        return mAmbientState.isDimmed();
+    }
+
     private void setDimAmount(float dimAmount) {
         mDimAmount = dimAmount;
         updateBackgroundDimming();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
new file mode 100644
index 0000000..1c010b6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.stack;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.UiThread;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.NotificationHeaderView;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationStackScrollLayoutTest extends SysuiTestCase {
+
+    private NotificationStackScrollLayout mStackScroller;
+    private StatusBar mBar;
+
+    @Before
+    @UiThreadTest
+    public void setUp() throws Exception {
+        mStackScroller = new NotificationStackScrollLayout(getContext());
+        mBar = mock(StatusBar.class);
+        mStackScroller.setStatusBar(mBar);
+        mStackScroller.setScrimController(mock(ScrimController.class));
+    }
+
+    @Test
+    public void testNotDimmedOnKeyguard() {
+        when(mBar.getBarState()).thenReturn(StatusBarState.SHADE);
+        mStackScroller.setDimmed(true /* dimmed */, false /* animate */);
+        mStackScroller.setDimmed(true /* dimmed */, true /* animate */);
+        Assert.assertFalse(mStackScroller.isDimmed());
+    }
+
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5c5a6b7..9727092 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8063,7 +8063,7 @@
                     stack.setPictureInPictureActions(actions);
 
                     MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED,
-                            r.supportsPictureInPictureWhilePausing);
+                            r.supportsEnterPipOnTaskSwitch);
                     logPictureInPictureArgs(params);
                 };
 
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 874bd1e..871ccb9 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -298,9 +298,8 @@
     boolean frozenBeforeDestroy;// has been frozen but not yet destroyed.
     boolean immersive;      // immersive mode (don't interrupt if possible)
     boolean forceNewConfig; // force re-create with new config next time
-    boolean supportsPictureInPictureWhilePausing;  // This flag is set by the system to indicate
-        // that the activity can enter picture in picture while pausing (ie. only when another
-        // task is brought to front or started)
+    boolean supportsEnterPipOnTaskSwitch;  // This flag is set by the system to indicate that the
+        // activity can enter picture in picture while pausing (only when switching to another task)
     PictureInPictureParams pictureInPictureArgs = new PictureInPictureParams.Builder().build();
         // The PiP params used when deferring the entering of picture-in-picture.
     int launchCount;        // count of launches since last state
@@ -545,8 +544,8 @@
                     + " mLastReportedPictureInPictureMode=" + mLastReportedPictureInPictureMode);
             if (info.supportsPictureInPicture()) {
                 pw.println(prefix + "supportsPictureInPicture=" + info.supportsPictureInPicture());
-                pw.println(prefix + "supportsPictureInPictureWhilePausing: "
-                        + supportsPictureInPictureWhilePausing);
+                pw.println(prefix + "supportsEnterPipOnTaskSwitch: "
+                        + supportsEnterPipOnTaskSwitch);
             }
             if (info.maxAspectRatio != 0) {
                 pw.println(prefix + "maxAspectRatio=" + info.maxAspectRatio);
@@ -1220,19 +1219,19 @@
                 // When visible, allow entering PiP if the app is not locked.  If it is over the
                 // keyguard, then we will prompt to unlock in the caller before entering PiP.
                 return !isCurrentAppLocked &&
-                        (supportsPictureInPictureWhilePausing || !beforeStopping);
+                        (supportsEnterPipOnTaskSwitch || !beforeStopping);
             case PAUSING:
             case PAUSED:
                 // When pausing, then only allow enter PiP as in the resume state, and in addition,
                 // require that there is not an existing PiP activity and that the current system
                 // state supports entering PiP
                 return isNotLockedOrOnKeyguard && !hasPinnedStack
-                        && supportsPictureInPictureWhilePausing;
+                        && supportsEnterPipOnTaskSwitch;
             case STOPPING:
                 // When stopping in a valid state, then only allow enter PiP as in the pause state.
                 // Otherwise, fall through to throw an exception if the caller is trying to enter
                 // PiP in an invalid stopping state.
-                if (supportsPictureInPictureWhilePausing) {
+                if (supportsEnterPipOnTaskSwitch) {
                     return isNotLockedOrOnKeyguard && !hasPinnedStack;
                 }
             default:
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index eb3177a..adf5a50 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2095,7 +2095,7 @@
 
                     // Reset the flag indicating that an app can enter picture-in-picture once the
                     // activity is hidden
-                    r.supportsPictureInPictureWhilePausing = false;
+                    r.supportsEnterPipOnTaskSwitch = false;
                     break;
 
                 case INITIALIZING:
@@ -2917,11 +2917,9 @@
                         // supporting picture-in-picture while pausing only if the starting activity
                         // would not be considered an overlay on top of the current activity
                         // (eg. not fullscreen, or the assistant)
-                        if (focusedTopActivity != null
-                                && focusedTopActivity.getStackId() != PINNED_STACK_ID
-                                && r.getStackId() != ASSISTANT_STACK_ID
-                                && r.fullscreen) {
-                            focusedTopActivity.supportsPictureInPictureWhilePausing = true;
+                        if (canEnterPipOnTaskSwitch(focusedTopActivity,
+                                null /* toFrontTask */, r, options)) {
+                            focusedTopActivity.supportsEnterPipOnTaskSwitch = true;
                         }
                         transit = TRANSIT_TASK_OPEN;
                     }
@@ -2976,6 +2974,37 @@
         }
     }
 
+    /**
+     * @return Whether the switch to another task can trigger the currently running activity to
+     * enter PiP while it is pausing (if supported). Only one of {@param toFrontTask} or
+     * {@param toFrontActivity} should be set.
+     */
+    private boolean canEnterPipOnTaskSwitch(ActivityRecord pipCandidate,
+            TaskRecord toFrontTask, ActivityRecord toFrontActivity, ActivityOptions opts) {
+        if (opts != null && opts.disallowEnterPictureInPictureWhileLaunching()) {
+            // Ensure the caller has requested not to trigger auto-enter PiP
+            return false;
+        }
+        if (pipCandidate == null || pipCandidate.getStackId() == PINNED_STACK_ID) {
+            // Ensure that we do not trigger entering PiP an activity on the pinned stack
+            return false;
+        }
+        final int targetStackId = toFrontTask != null ? toFrontTask.getStackId()
+                : toFrontActivity.getStackId();
+        if (targetStackId == ASSISTANT_STACK_ID) {
+            // Ensure the task/activity being brought forward is not the assistant
+            return false;
+        }
+        final boolean isFullscreen = toFrontTask != null
+                ? toFrontTask.containsOnlyFullscreenActivities()
+                : toFrontActivity.fullscreen;
+        if (!isFullscreen) {
+            // Ensure the task/activity being brought forward is fullscreen
+            return false;
+        }
+        return true;
+    }
+
     private boolean isTaskSwitch(ActivityRecord r,
             ActivityRecord topFocusedActivity) {
         return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask();
@@ -4551,9 +4580,9 @@
         // If a new task is moved to the front, then mark the existing top activity as supporting
         // picture-in-picture while paused only if the task would not be considered an oerlay on top
         // of the current activity (eg. not fullscreen, or the assistant)
-        if (topActivity != null && topActivity.getStackId() != PINNED_STACK_ID
-                && tr.getStackId() != ASSISTANT_STACK_ID && tr.containsOnlyFullscreenActivities()) {
-            topActivity.supportsPictureInPictureWhilePausing = true;
+        if (canEnterPipOnTaskSwitch(topActivity, tr, null /* toFrontActivity */,
+                options)) {
+            topActivity.supportsEnterPipOnTaskSwitch = true;
         }
 
         mStackSupervisor.resumeFocusedStackTopActivityLocked();
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 68a4ad9..4eedb25 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.Manifest.permission.ACTIVITY_EMBEDDING;
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.START_ANY_ACTIVITY;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
@@ -35,6 +36,7 @@
 import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
@@ -57,7 +59,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONTAINERS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IDLE;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
@@ -135,7 +136,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -1651,7 +1651,7 @@
             if (options.getLaunchTaskId() != INVALID_STACK_ID) {
                 final int startInTaskPerm = mService.checkPermission(START_TASKS_FROM_RECENTS,
                         callingPid, callingUid);
-                if (startInTaskPerm != PERMISSION_GRANTED) {
+                if (startInTaskPerm == PERMISSION_DENIED) {
                     final String msg = "Permission Denial: starting " + intent.toString()
                             + " from " + callerApp + " (pid=" + callingPid
                             + ", uid=" + callingUid + ") with launchTaskId="
@@ -1704,9 +1704,9 @@
             return true;
         }
 
-        if (activityDisplay.mDisplay.getType() == TYPE_VIRTUAL
-                && activityDisplay.mDisplay.getOwnerUid() != SYSTEM_UID
-                && activityDisplay.mDisplay.getOwnerUid() != aInfo.applicationInfo.uid) {
+        final int displayOwnerUid = activityDisplay.mDisplay.getOwnerUid();
+        if (activityDisplay.mDisplay.getType() == TYPE_VIRTUAL && displayOwnerUid != SYSTEM_UID
+                && displayOwnerUid != aInfo.applicationInfo.uid) {
             // Limit launching on virtual displays, because their contents can be read from Surface
             // by apps that created them.
             if ((aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
@@ -1714,6 +1714,13 @@
                         + " disallow launch on virtual display for not-embedded activity.");
                 return false;
             }
+            // Check if the caller is allowed to embed activities from other apps.
+            if (mService.checkPermission(ACTIVITY_EMBEDDING, callingPid, callingUid)
+                    == PERMISSION_DENIED) {
+                if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+                        + " disallow activity embedding without permission.");
+                return false;
+            }
         }
 
         if (!activityDisplay.isPrivate()) {
@@ -1724,7 +1731,7 @@
         }
 
         // Check if the caller is the owner of the display.
-        if (activityDisplay.mDisplay.getOwnerUid() == callingUid) {
+        if (displayOwnerUid == callingUid) {
             if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
                     + " allow launch for owner of the display");
             return true;
@@ -1769,7 +1776,7 @@
             String callingPackage, int callingPid, int callingUid, boolean ignoreTargetSecurity) {
         if (!ignoreTargetSecurity && mService.checkComponentPermission(activityInfo.permission,
                 callingPid, callingUid, activityInfo.applicationInfo.uid, activityInfo.exported)
-                == PackageManager.PERMISSION_DENIED) {
+                == PERMISSION_DENIED) {
             return ACTIVITY_RESTRICTION_PERMISSION;
         }
 
@@ -1816,8 +1823,7 @@
             return ACTIVITY_RESTRICTION_NONE;
         }
 
-        if (mService.checkPermission(permission, callingPid, callingUid) ==
-                PackageManager.PERMISSION_DENIED) {
+        if (mService.checkPermission(permission, callingPid, callingUid) == PERMISSION_DENIED) {
             return ACTIVITY_RESTRICTION_PERMISSION;
         }
 
@@ -2984,7 +2990,7 @@
                 // while pausing because that changes the focused stack and may prevent the new
                 // starting activity from resuming.
                 if (moveHomeStackToFront && task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE
-                        && (r.state == RESUMED || !r.supportsPictureInPictureWhilePausing)) {
+                        && (r.state == RESUMED || !r.supportsEnterPipOnTaskSwitch)) {
                     // Move the home stack forward if the task we just moved to the pinned stack
                     // was launched from home so home should be visible behind it.
                     moveHomeStackToFront(reason);
@@ -3013,7 +3019,7 @@
 
             // Reset the state that indicates it can enter PiP while pausing after we've moved it
             // to the pinned stack
-            r.supportsPictureInPictureWhilePausing = false;
+            r.supportsEnterPipOnTaskSwitch = false;
         } finally {
             mWindowManager.continueSurfaceLayout();
         }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 51ed6b2..2f3b559 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -99,6 +99,7 @@
 import com.android.server.content.SyncStorageEngine.AuthorityInfo;
 import com.android.server.content.SyncStorageEngine.EndPoint;
 import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
+import com.android.server.job.JobSchedulerInternal.JobStorePersistStats;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -403,12 +404,21 @@
         return (networkInfo != null) && networkInfo.isConnected();
     }
 
+    private String getJobStats() {
+        JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
+        return "JobStats: "
+                + ((js == null) ? "(JobSchedulerInternal==null)"
+                : js.getPersistStats().toString());
+    }
+
     private BroadcastReceiver mShutdownIntentReceiver =
             new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
                     Log.w(TAG, "Writing sync state before shutdown...");
                     getSyncStorageEngine().writeAllState();
+
+                    mLogger.log(getJobStats());
                     mLogger.log("Shutting down.");
                 }
             };
@@ -504,13 +514,11 @@
                     }
                 }
             }
-            final int totalJobs = (mJobSchedulerInternal == null)
-                    ? -1 : mJobSchedulerInternal.countJobs();
             final String summary = "Loaded persisted syncs: "
                     + numPersistedPeriodicSyncs + " periodic syncs, "
                     + numPersistedOneshotSyncs + " oneshot syncs, "
                     + (pendingJobs.size()) + " total system server jobs, "
-                    + totalJobs + " total jobs.";
+                    + getJobStats();
             Slog.i(TAG, summary);
             mLogger.log(summary);
 
@@ -720,7 +728,7 @@
         // the account (they run before) which is the genie is out of the bottle.
         whiteListExistingSyncAdaptersIfNeeded();
 
-        mLogger.log("Sync manager initialized.");
+        mLogger.log("Sync manager initialized: " + Build.FINGERPRINT);
     }
 
     public void onStartUser(int userHandle) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 8269042..d0a1d9e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -31,6 +31,8 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Point;
 import android.hardware.SensorManager;
 import android.hardware.display.DisplayManagerGlobal;
 import android.hardware.display.DisplayManagerInternal;
@@ -211,6 +213,12 @@
     // The virtual display adapter, or null if not registered.
     private VirtualDisplayAdapter mVirtualDisplayAdapter;
 
+    // The stable device screen height and width. These are not tied to a specific display, even
+    // the default display, because they need to be stable over the course of the device's entire
+    // life, even if the default display changes (e.g. a new monitor is plugged into a PC-like
+    // device).
+    private Point mStableDisplaySize = new Point();
+
     // Viewports of the default display and the display that should receive touch
     // input from an external source.  Used by the input system.
     private final DisplayViewport mDefaultViewport = new DisplayViewport();
@@ -284,7 +292,10 @@
         // adapter is up so that we have it's configuration. We could load it lazily, but since
         // we're going to have to read it in eventually we may as well do it here rather than after
         // we've waited for the display to register itself with us.
-        mPersistentDataStore.loadIfNeeded();
+		synchronized(mSyncRoot) {
+			mPersistentDataStore.loadIfNeeded();
+			loadStableDisplayValuesLocked();
+        }
         mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS);
 
         publishBinderService(Context.DISPLAY_SERVICE, new BinderService(),
@@ -346,6 +357,34 @@
         return mHandler;
     }
 
+    private void loadStableDisplayValuesLocked() {
+        final Point size = mPersistentDataStore.getStableDisplaySize();
+        if (size.x > 0 && size.y > 0) {
+            // Just set these values directly so we don't write the display persistent data again
+            // unnecessarily
+            mStableDisplaySize.set(size.x, size.y);
+        } else {
+            final Resources res = mContext.getResources();
+            final int width = res.getInteger(
+                    com.android.internal.R.integer.config_stableDeviceDisplayWidth);
+            final int height = res.getInteger(
+                    com.android.internal.R.integer.config_stableDeviceDisplayHeight);
+            if (width > 0 && height > 0) {
+                setStableDisplaySizeLocked(width, height);
+            }
+        }
+    }
+
+    private Point getStableDisplaySizeInternal() {
+        Point r = new Point();
+        synchronized (mSyncRoot) {
+            if (mStableDisplaySize.x > 0 && mStableDisplaySize.y > 0) {
+                r.set(mStableDisplaySize.x, mStableDisplaySize.y);
+            }
+        }
+        return r;
+    }
+
     private void registerDisplayTransactionListenerInternal(
             DisplayTransactionListener listener) {
         // List is self-synchronized copy-on-write.
@@ -770,18 +809,6 @@
         if (work != null) {
             work.run();
         }
-        if (display != null && display.getPrimaryDisplayDeviceLocked() == device) {
-            int colorMode = mPersistentDataStore.getColorMode(device);
-            if (colorMode == Display.COLOR_MODE_INVALID) {
-                if ((device.getDisplayDeviceInfoLocked().flags
-                     & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
-                    colorMode = mDefaultDisplayDefaultColorMode;
-                } else {
-                    colorMode = Display.COLOR_MODE_DEFAULT;
-                }
-            }
-            display.setRequestedColorModeLocked(colorMode);
-        }
         scheduleTraversalLocked(false);
     }
 
@@ -886,6 +913,11 @@
             return null;
         }
 
+        configureColorModeLocked(display, device);
+        if (isDefault) {
+            recordStableDisplayStatsIfNeededLocked(display);
+        }
+
         mLogicalDisplays.put(displayId, display);
 
         // Wake up waitForDefaultDisplay.
@@ -907,6 +939,40 @@
         return displayId;
     }
 
+    private void configureColorModeLocked(LogicalDisplay display, DisplayDevice device) {
+        if (display.getPrimaryDisplayDeviceLocked() == device) {
+            int colorMode = mPersistentDataStore.getColorMode(device);
+            if (colorMode == Display.COLOR_MODE_INVALID) {
+                if ((device.getDisplayDeviceInfoLocked().flags
+                     & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
+                    colorMode = mDefaultDisplayDefaultColorMode;
+                } else {
+                    colorMode = Display.COLOR_MODE_DEFAULT;
+                }
+            }
+            display.setRequestedColorModeLocked(colorMode);
+        }
+    }
+
+    // If we've never recorded stable device stats for this device before and they aren't
+    // explicitly configured, go ahead and record the stable device stats now based on the status
+    // of the default display at first boot.
+    private void recordStableDisplayStatsIfNeededLocked(LogicalDisplay d) {
+        if (mStableDisplaySize.x <= 0 && mStableDisplaySize.y <= 0) {
+            DisplayInfo info = d.getDisplayInfoLocked();
+            setStableDisplaySizeLocked(info.getNaturalWidth(), info.getNaturalHeight());
+        }
+    }
+
+    private void setStableDisplaySizeLocked(int width, int height) {
+        mStableDisplaySize = new Point(width, height);
+        try {
+            mPersistentDataStore.setStableDisplaySize(mStableDisplaySize);
+        } finally {
+            mPersistentDataStore.saveIfNeeded();
+        }
+    }
+
     // Updates all existing logical displays given the current set of display devices.
     // Removes invalid logical displays.
     // Sends notifications if needed.
@@ -1166,6 +1232,8 @@
             pw.println("  mDefaultDisplayDefaultColorMode=" + mDefaultDisplayDefaultColorMode);
             pw.println("  mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
             pw.println("  mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount);
+            pw.println("  mStableDisplaySize=" + mStableDisplaySize);
+
 
             IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
             ipw.increaseIndent();
@@ -1378,6 +1446,19 @@
             }
         }
 
+        /**
+         * Returns the stable device display size, in pixels.
+         */
+        @Override // Binder call
+        public Point getStableDisplaySize() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return getStableDisplaySizeInternal();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         @Override // Binder call
         public void registerCallback(IDisplayManagerCallback callback) {
             if (callback == null) {
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 47701b9..34c8e22 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -23,6 +23,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import android.graphics.Point;
 import android.hardware.display.WifiDisplay;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -60,6 +61,10 @@
  *          &lt;color-mode>0&lt;/color-mode>
  *      &lt;/display>
  *  &lt;/display-states>
+ *  &lt;stable-device-values>
+ *      &lt;stable-display-height>1920&lt;stable-display-height>
+ *      &lt;stable-display-width>1080&lt;stable-display-width>
+ *  &lt;/stable-device-values>
  * &lt;/display-manager-state>
  * </code>
  *
@@ -75,6 +80,9 @@
     private final HashMap<String, DisplayState> mDisplayStates =
             new HashMap<String, DisplayState>();
 
+    // Display values which should be stable across the device's lifetime.
+    private final StableDeviceValues mStableDeviceValues = new StableDeviceValues();
+
     // The atomic file used to safely read or write the file.
     private final AtomicFile mAtomicFile;
 
@@ -162,6 +170,7 @@
     }
 
     public boolean forgetWifiDisplay(String deviceAddress) {
+		loadIfNeeded();
         int index = findRememberedWifiDisplay(deviceAddress);
         if (index >= 0) {
             mRememberedWifiDisplays.remove(index);
@@ -204,6 +213,18 @@
         return false;
     }
 
+	public Point getStableDisplaySize() {
+		loadIfNeeded();
+		return mStableDeviceValues.getDisplaySize();
+	}
+
+	public void setStableDisplaySize(Point size) {
+		loadIfNeeded();
+		if (mStableDeviceValues.setDisplaySize(size)) {
+			setDirty();
+		}
+	}
+
     private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) {
         loadIfNeeded();
         DisplayState state = mDisplayStates.get(uniqueId);
@@ -290,6 +311,9 @@
             if (parser.getName().equals("display-states")) {
                 loadDisplaysFromXml(parser);
             }
+            if (parser.getName().equals("stable-device-values")) {
+                mStableDeviceValues.loadFromXml(parser);
+            }
         }
     }
 
@@ -363,6 +387,9 @@
             serializer.endTag(null, "display");
         }
         serializer.endTag(null, "display-states");
+        serializer.startTag(null, "stable-device-values");
+        mStableDeviceValues.saveToXml(serializer);
+        serializer.endTag(null, "stable-device-values");
         serializer.endTag(null, "display-manager-state");
         serializer.endDocument();
     }
@@ -382,6 +409,8 @@
             pw.println("    " + i++ + ": " + entry.getKey());
             entry.getValue().dump(pw, "      ");
         }
+        pw.println("  StableDeviceValues:");
+        mStableDeviceValues.dump(pw, "      ");
     }
 
     private static final class DisplayState {
@@ -417,8 +446,66 @@
             serializer.endTag(null, "color-mode");
         }
 
-        private void dump(final PrintWriter pw, final String prefix) {
+        public void dump(final PrintWriter pw, final String prefix) {
             pw.println(prefix + "ColorMode=" + mColorMode);
         }
     }
+
+    private static final class StableDeviceValues {
+        private int mWidth;
+        private int mHeight;
+
+        private Point getDisplaySize() {
+            return new Point(mWidth, mHeight);
+        }
+
+        public boolean setDisplaySize(Point r) {
+            if (mWidth != r.x || mHeight != r.y) {
+                mWidth = r.x;
+                mHeight = r.y;
+                return true;
+            }
+            return false;
+        }
+
+        public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
+            final int outerDepth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                switch (parser.getName()) {
+                    case "stable-display-width":
+                        mWidth = loadIntValue(parser);
+                        break;
+                    case "stable-display-height":
+                        mHeight = loadIntValue(parser);
+                        break;
+                }
+            }
+        }
+
+        private static int loadIntValue(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+            try {
+                String value = parser.nextText();
+                return Integer.parseInt(value);
+            } catch (NumberFormatException nfe) {
+                return 0;
+            }
+        }
+
+        public void saveToXml(XmlSerializer serializer) throws IOException {
+            if (mWidth > 0 && mHeight > 0) {
+                serializer.startTag(null, "stable-display-width");
+                serializer.text(Integer.toString(mWidth));
+                serializer.endTag(null, "stable-display-width");
+                serializer.startTag(null, "stable-display-height");
+                serializer.text(Integer.toString(mHeight));
+                serializer.endTag(null, "stable-display-height");
+            }
+        }
+
+        public void dump(final PrintWriter pw, final String prefix) {
+            pw.println(prefix + "StableDisplayWidth=" + mWidth);
+            pw.println(prefix + "StableDisplayHeight=" + mHeight);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/job/JobSchedulerInternal.java b/services/core/java/com/android/server/job/JobSchedulerInternal.java
index d52b3b7..095526d 100644
--- a/services/core/java/com/android/server/job/JobSchedulerInternal.java
+++ b/services/core/java/com/android/server/job/JobSchedulerInternal.java
@@ -38,8 +38,44 @@
     void removeBackingUpUid(int uid);
     void clearAllBackingUpUids();
 
+    JobStorePersistStats getPersistStats();
+
     /**
-     * @return the total number of jobs across all UIDs.
+     * Stats about the first load after boot and the most recent save.
+     * STOPSHIP Remove it and the relevant code once b/64536115 is fixed.
      */
-    int countJobs();
+    public class JobStorePersistStats {
+        public int countAllJobsLoaded = -1;
+        public int countSystemServerJobsLoaded = -1;
+        public int countSystemSyncManagerJobsLoaded = -1;
+
+        public int countAllJobsSaved = -1;
+        public int countSystemServerJobsSaved = -1;
+        public int countSystemSyncManagerJobsSaved = -1;
+
+        public JobStorePersistStats() {
+        }
+
+        public JobStorePersistStats(JobStorePersistStats source) {
+            countAllJobsLoaded = source.countAllJobsLoaded;
+            countSystemServerJobsLoaded = source.countSystemServerJobsLoaded;
+            countSystemSyncManagerJobsLoaded = source.countSystemSyncManagerJobsLoaded;
+
+            countAllJobsSaved = source.countAllJobsSaved;
+            countSystemServerJobsSaved = source.countSystemServerJobsSaved;
+            countSystemSyncManagerJobsSaved = source.countSystemSyncManagerJobsSaved;
+        }
+
+        @Override
+        public String toString() {
+            return "FirstLoad: "
+                    + countAllJobsLoaded + "/"
+                    + countSystemServerJobsLoaded + "/"
+                    + countSystemSyncManagerJobsLoaded
+                    + " LastSave: "
+                    + countAllJobsSaved + "/"
+                    + countSystemServerJobsSaved + "/"
+                    + countSystemSyncManagerJobsSaved;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 12087b0..3fc76c0 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -782,6 +782,11 @@
      *
      */
     public void cancelJobsForUid(int uid, String reason) {
+        if (uid == Process.SYSTEM_UID) {
+            // This really shouldn't happen.
+            Slog.wtfStack(TAG, "cancelJobsForUid() called for system uid");
+            return;
+        }
         synchronized (mLock) {
             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
             for (int i=0; i<jobsForUid.size(); i++) {
@@ -1837,9 +1842,9 @@
         }
 
         @Override
-        public int countJobs() {
+        public JobStorePersistStats getPersistStats() {
             synchronized (mLock) {
-                return mJobs.size();
+                return new JobStorePersistStats(mJobs.getPersistStats());
             }
         }
     }
@@ -2022,13 +2027,6 @@
         @Override
         public void cancelAll() throws RemoteException {
             final int uid = Binder.getCallingUid();
-            switch (uid) {
-                case Process.SYSTEM_UID:
-                    // This really shouldn't happen.
-                    Slog.wtf(TAG, "JobScheduler.cancelAll() called for uid=" + uid);
-                    return;
-            }
-
             long ident = Binder.clearCallingIdentity();
             try {
                 JobSchedulerService.this.cancelJobsForUid(uid, "cancelAll() called by app");
@@ -2470,6 +2468,9 @@
                 pw.print("mReportedActive="); pw.println(mReportedActive);
                 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
             }
+            pw.println();
+            pw.print("PersistStats: ");
+            pw.println(mJobs.getPersistStats());
         }
         pw.println();
     }
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 5317616..aa9f77c 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -24,6 +24,7 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.format.DateUtils;
@@ -38,6 +39,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.server.IoThread;
+import com.android.server.job.JobSchedulerInternal.JobStorePersistStats;
 import com.android.server.job.controllers.JobStatus;
 
 import java.io.ByteArrayOutputStream;
@@ -89,6 +91,8 @@
     private final Handler mIoHandler = IoThread.getHandler();
     private static JobStore sSingleton;
 
+    private JobStorePersistStats mPersistInfo = new JobStorePersistStats();
+
     /** Used by the {@link JobSchedulerService} to instantiate the JobStore. */
     static JobStore initAndGet(JobSchedulerService jobManagerService) {
         synchronized (sSingletonLock) {
@@ -199,6 +203,10 @@
         return mJobSet.size();
     }
 
+    public JobStorePersistStats getPersistStats() {
+        return mPersistInfo;
+    }
+
     public int countJobsForUid(int uid) {
         return mJobSet.countJobsForUid(uid);
     }
@@ -336,6 +344,9 @@
         }
 
         private void writeJobsMapImpl(List<JobStatus> jobList) {
+            int numJobs = 0;
+            int numSystemJobs = 0;
+            int numSyncJobs = 0;
             try {
                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
                 XmlSerializer out = new FastXmlSerializer();
@@ -356,6 +367,14 @@
                     writeExecutionCriteriaToXml(out, jobStatus);
                     writeBundleToXml(jobStatus.getJob().getExtras(), out);
                     out.endTag(null, "job");
+
+                    numJobs++;
+                    if (jobStatus.getUid() == Process.SYSTEM_UID) {
+                        numSystemJobs++;
+                        if (isSyncJob(jobStatus)) {
+                            numSyncJobs++;
+                        }
+                    }
                 }
                 out.endTag(null, "job-info");
                 out.endDocument();
@@ -373,6 +392,10 @@
                 if (DEBUG) {
                     Slog.d(TAG, "Error persisting bundle.", e);
                 }
+            } finally {
+                mPersistInfo.countAllJobsSaved = numJobs;
+                mPersistInfo.countSystemServerJobsSaved = numSystemJobs;
+                mPersistInfo.countSystemSyncManagerJobsSaved = numSyncJobs;
             }
         }
 
@@ -525,6 +548,11 @@
         return Pair.create(earliest, latest);
     }
 
+    private static boolean isSyncJob(JobStatus status) {
+        return com.android.server.content.SyncJobService.class.getName()
+                .equals(status.getServiceComponent().getClassName());
+    }
+
     /**
      * Runnable that reads list of persisted job from xml. This is run once at start up, so doesn't
      * need to go through {@link JobStore#add(com.android.server.job.controllers.JobStatus)}.
@@ -545,6 +573,8 @@
         @Override
         public void run() {
             int numJobs = 0;
+            int numSystemJobs = 0;
+            int numSyncJobs = 0;
             try {
                 List<JobStatus> jobs;
                 FileInputStream fis = mJobsFile.openRead();
@@ -558,7 +588,14 @@
                             js.prepareLocked(am);
                             js.enqueueTime = now;
                             this.jobSet.add(js);
+
                             numJobs++;
+                            if (js.getUid() == Process.SYSTEM_UID) {
+                                numSystemJobs++;
+                                if (isSyncJob(js)) {
+                                    numSyncJobs++;
+                                }
+                            }
                         }
                     }
                 }
@@ -569,6 +606,12 @@
                 }
             } catch (XmlPullParserException | IOException e) {
                 Slog.wtf(TAG, "Error jobstore xml.", e);
+            } finally {
+                if (mPersistInfo.countAllJobsLoaded < 0) { // Only set them once.
+                    mPersistInfo.countAllJobsLoaded = numJobs;
+                    mPersistInfo.countSystemServerJobsLoaded = numSystemJobs;
+                    mPersistInfo.countSystemSyncManagerJobsLoaded = numSyncJobs;
+                }
             }
             Slog.i(TAG, "Read " + numJobs + " jobs");
         }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index d39679d..4d2cf32 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -37,6 +37,7 @@
     private static final String COMMAND_SP = "sp";
     private static final String COMMAND_SET_DISABLED = "set-disabled";
     private static final String COMMAND_VERIFY = "verify";
+    private static final String COMMAND_GET_DISABLED = "get-disabled";
 
     private int mCurrentUserId;
     private final LockPatternUtils mLockPatternUtils;
@@ -80,6 +81,9 @@
                 case COMMAND_VERIFY:
                     runVerify();
                     break;
+                case COMMAND_GET_DISABLED:
+                    runGetDisabled();
+                    break;
                 default:
                     getErrPrintWriter().println("Unknown command: " + cmd);
                     break;
@@ -156,6 +160,11 @@
         getOutPrintWriter().println("Lock screen disabled set to " + disabled);
     }
 
+    private void runGetDisabled() {
+        boolean isLockScreenDisabled = mLockPatternUtils.isLockScreenDisabled(mCurrentUserId);
+        getOutPrintWriter().println(isLockScreenDisabled);
+    }
+
     private boolean checkCredential() throws RemoteException {
         final boolean havePassword = mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId);
         final boolean havePattern = mLockPatternUtils.isLockPatternEnabled(mCurrentUserId);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index d5d0250..34f1bfa 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -455,6 +455,11 @@
         }
     }
 
+    public void onUserRemoved(int user) {
+        mApproved.remove(user);
+        rebindServices(true);
+    }
+
     public void onUserSwitched(int user) {
         if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
         if (Arrays.equals(mLastSeenProfileIds, mUserProfiles.getCurrentProfileIds())) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f60d923..3be998d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -985,12 +985,17 @@
                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
                 if (userId != USER_NULL) {
                     mUserProfiles.updateCache(context);
-                    readDefaultApprovedServices(userId);
+                    if (!mUserProfiles.isManagedProfile(userId)) {
+                        readDefaultApprovedServices(userId);
+                    }
                 }
             } else if (action.equals(Intent.ACTION_USER_REMOVED)) {
                 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
                 mZenModeHelper.onUserRemoved(user);
                 mRankingHelper.onUserRemoved(user);
+                mListeners.onUserRemoved(user);
+                mConditionProviders.onUserRemoved(user);
+                mAssistants.onUserRemoved(user);
                 savePolicyFile();
             } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
                 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 23d36988..9d0b44d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -21822,32 +21822,36 @@
             }
         }
 
-        if (callingUid == Process.SHELL_UID
-                && (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
-            // Shell can only change whole packages between ENABLED and DISABLED_USER states
-            // unless it is a test package.
-            int oldState = pkgSetting.getEnabled(userId);
-            if (className == null
-                &&
-                (oldState == COMPONENT_ENABLED_STATE_DISABLED_USER
-                 || oldState == COMPONENT_ENABLED_STATE_DEFAULT
-                 || oldState == COMPONENT_ENABLED_STATE_ENABLED)
-                &&
-                (newState == COMPONENT_ENABLED_STATE_DISABLED_USER
-                 || newState == COMPONENT_ENABLED_STATE_DEFAULT
-                 || newState == COMPONENT_ENABLED_STATE_ENABLED)) {
-                // ok
-            } else {
-                throw new SecurityException(
-                        "Shell cannot change component state for " + packageName + "/"
-                        + className + " to " + newState);
+        synchronized (mPackages) {
+            if (callingUid == Process.SHELL_UID
+                    && (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
+                // Shell can only change whole packages between ENABLED and DISABLED_USER states
+                // unless it is a test package.
+                int oldState = pkgSetting.getEnabled(userId);
+                if (className == null
+                        &&
+                        (oldState == COMPONENT_ENABLED_STATE_DISABLED_USER
+                                || oldState == COMPONENT_ENABLED_STATE_DEFAULT
+                                || oldState == COMPONENT_ENABLED_STATE_ENABLED)
+                        &&
+                        (newState == COMPONENT_ENABLED_STATE_DISABLED_USER
+                                || newState == COMPONENT_ENABLED_STATE_DEFAULT
+                                || newState == COMPONENT_ENABLED_STATE_ENABLED)) {
+                    // ok
+                } else {
+                    throw new SecurityException(
+                            "Shell cannot change component state for " + packageName + "/"
+                                    + className + " to " + newState);
+                }
             }
         }
         if (className == null) {
             // We're dealing with an application/package level state change
-            if (pkgSetting.getEnabled(userId) == newState) {
-                // Nothing to do
-                return;
+            synchronized (mPackages) {
+                if (pkgSetting.getEnabled(userId) == newState) {
+                    // Nothing to do
+                    return;
+                }
             }
             // If we're enabling a system stub, there's a little more work to do.
             // Prior to enabling the package, we need to decompress the APK(s) to the
@@ -21961,41 +21965,45 @@
                 // Don't care about who enables an app.
                 callingPackage = null;
             }
-            pkgSetting.setEnabled(newState, userId, callingPackage);
-        } else {
-            // We're dealing with a component level state change
-            // First, verify that this is a valid class name.
-            PackageParser.Package pkg = pkgSetting.pkg;
-            if (pkg == null || !pkg.hasComponentClassName(className)) {
-                if (pkg != null &&
-                        pkg.applicationInfo.targetSdkVersion >=
-                                Build.VERSION_CODES.JELLY_BEAN) {
-                    throw new IllegalArgumentException("Component class " + className
-                            + " does not exist in " + packageName);
-                } else {
-                    Slog.w(TAG, "Failed setComponentEnabledSetting: component class "
-                            + className + " does not exist in " + packageName);
-                }
+            synchronized (mPackages) {
+                pkgSetting.setEnabled(newState, userId, callingPackage);
             }
-            switch (newState) {
-            case COMPONENT_ENABLED_STATE_ENABLED:
-                if (!pkgSetting.enableComponentLPw(className, userId)) {
-                    return;
+        } else {
+            synchronized (mPackages) {
+                // We're dealing with a component level state change
+                // First, verify that this is a valid class name.
+                PackageParser.Package pkg = pkgSetting.pkg;
+                if (pkg == null || !pkg.hasComponentClassName(className)) {
+                    if (pkg != null &&
+                            pkg.applicationInfo.targetSdkVersion >=
+                                    Build.VERSION_CODES.JELLY_BEAN) {
+                        throw new IllegalArgumentException("Component class " + className
+                                + " does not exist in " + packageName);
+                    } else {
+                        Slog.w(TAG, "Failed setComponentEnabledSetting: component class "
+                                + className + " does not exist in " + packageName);
+                    }
                 }
-                break;
-            case COMPONENT_ENABLED_STATE_DISABLED:
-                if (!pkgSetting.disableComponentLPw(className, userId)) {
-                    return;
+                switch (newState) {
+                    case COMPONENT_ENABLED_STATE_ENABLED:
+                        if (!pkgSetting.enableComponentLPw(className, userId)) {
+                            return;
+                        }
+                        break;
+                    case COMPONENT_ENABLED_STATE_DISABLED:
+                        if (!pkgSetting.disableComponentLPw(className, userId)) {
+                            return;
+                        }
+                        break;
+                    case COMPONENT_ENABLED_STATE_DEFAULT:
+                        if (!pkgSetting.restoreComponentLPw(className, userId)) {
+                            return;
+                        }
+                        break;
+                    default:
+                        Slog.e(TAG, "Invalid new component state: " + newState);
+                        return;
                 }
-                break;
-            case COMPONENT_ENABLED_STATE_DEFAULT:
-                if (!pkgSetting.restoreComponentLPw(className, userId)) {
-                    return;
-                }
-                break;
-            default:
-                Slog.e(TAG, "Invalid new component state: " + newState);
-                return;
             }
         }
         synchronized (mPackages) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f67ebe4..4c2e469 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5552,6 +5552,7 @@
             WindowManager.LayoutParams statusBarAttrs = mStatusBar.getAttrs();
             boolean statusBarExpanded = statusBarAttrs.height == MATCH_PARENT
                     && statusBarAttrs.width == MATCH_PARENT;
+            boolean topAppHidesStatusBar = topAppHidesStatusBar();
             if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent
                     || statusBarExpanded) {
                 if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced");
@@ -5572,16 +5573,7 @@
                     }
                 }
             } else if (mTopFullscreenOpaqueWindowState != null) {
-                final int fl = PolicyControl.getWindowFlags(null, lp);
-                if (localLOGV) {
-                    Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
-                            + " shown position: "
-                            + mTopFullscreenOpaqueWindowState.getShownPositionLw());
-                    Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
-                            + " lp.flags=0x" + Integer.toHexString(fl));
-                }
-                topIsFullscreen = (fl & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0
-                        || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
+                topIsFullscreen = topAppHidesStatusBar;
                 // The subtle difference between the window for mTopFullscreenOpaqueWindowState
                 // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window
                 // has the FLAG_FULLSCREEN set.  Not sure if there is another way that to be the
@@ -5604,8 +5596,10 @@
                     if (mStatusBarController.setBarShowingLw(true)) {
                         changes |= FINISH_LAYOUT_REDO_LAYOUT;
                     }
+                    topAppHidesStatusBar = false;
                 }
             }
+            mStatusBarController.setTopAppHidesStatusBar(topAppHidesStatusBar);
         }
 
         if (mTopIsFullscreen != topIsFullscreen) {
@@ -5646,6 +5640,27 @@
     }
 
     /**
+     * @return Whether the top app should hide the statusbar based on the top fullscreen opaque
+     *         window.
+     */
+    private boolean topAppHidesStatusBar() {
+        if (mTopFullscreenOpaqueWindowState == null) {
+            return false;
+        }
+        final int fl = PolicyControl.getWindowFlags(null,
+                mTopFullscreenOpaqueWindowState.getAttrs());
+        if (localLOGV) {
+            Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
+                    + " shown position: "
+                    + mTopFullscreenOpaqueWindowState.getShownPositionLw());
+            Slog.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
+                    + " lp.flags=0x" + Integer.toHexString(fl));
+        }
+        return (fl & LayoutParams.FLAG_FULLSCREEN) != 0
+                || (mLastSystemUiFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
+    }
+
+    /**
      * Updates the occluded state of the Keyguard.
      *
      * @return Whether the flags have changed and we have to redo the layout.
diff --git a/services/core/java/com/android/server/policy/StatusBarController.java b/services/core/java/com/android/server/policy/StatusBarController.java
index 7d67b60..ecc88b5 100644
--- a/services/core/java/com/android/server/policy/StatusBarController.java
+++ b/services/core/java/com/android/server/policy/StatusBarController.java
@@ -112,6 +112,14 @@
                 View.STATUS_BAR_TRANSPARENT);
     }
 
+
+    public void setTopAppHidesStatusBar(boolean hidesStatusBar) {
+        StatusBarManagerInternal statusbar = getStatusBarInternal();
+        if (statusbar != null) {
+            statusbar.setTopAppHidesStatusBar(hidesStatusBar);
+        }
+    }
+
     @Override
     protected boolean skipAnimation() {
         return mWin.getAttrs().height == MATCH_PARENT;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 866fdad..0884678 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -80,6 +80,13 @@
     void setGlobalActionsListener(GlobalActionsListener listener);
     void showGlobalActions();
 
+    /**
+     * Set whether the top app currently hides the statusbar.
+     *
+     * @param hidesStatusBar whether it is being hidden
+     */
+    void setTopAppHidesStatusBar(boolean hidesStatusBar);
+
     boolean showShutdownUi(boolean isReboot, String requestString);
 
     public interface GlobalActionsListener {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index d31c230..34ac645 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -332,6 +332,15 @@
         }
 
         @Override
+        public void setTopAppHidesStatusBar(boolean hidesStatusBar) {
+            if (mBar != null) {
+                try {
+                    mBar.setTopAppHidesStatusBar(hidesStatusBar);
+                } catch (RemoteException ex) {}
+            }
+        }
+
+        @Override
         public boolean showShutdownUi(boolean isReboot, String reason) {
             if (!mContext.getResources().getBoolean(R.bool.config_showSysuiShutdown)) {
                 return false;
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index b6b964b..1f0b2f0 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -74,6 +74,7 @@
 import java.io.PrintWriter;
 import java.lang.StringBuilder;
 import java.text.SimpleDateFormat;
+import java.util.Arrays;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -414,7 +415,8 @@
 
         @Override
         public void registerListener(IVrStateCallbacks cb) {
-            enforceCallerPermission(Manifest.permission.ACCESS_VR_MANAGER);
+            enforceCallerPermissionAnyOf(Manifest.permission.ACCESS_VR_MANAGER,
+                    Manifest.permission.ACCESS_VR_STATE);
             if (cb == null) {
                 throw new IllegalArgumentException("Callback binder object is null.");
             }
@@ -424,7 +426,8 @@
 
         @Override
         public void unregisterListener(IVrStateCallbacks cb) {
-            enforceCallerPermission(Manifest.permission.ACCESS_VR_MANAGER);
+            enforceCallerPermissionAnyOf(Manifest.permission.ACCESS_VR_MANAGER,
+                    Manifest.permission.ACCESS_VR_STATE);
             if (cb == null) {
                 throw new IllegalArgumentException("Callback binder object is null.");
             }
@@ -434,7 +437,8 @@
 
         @Override
         public void registerPersistentVrStateListener(IPersistentVrStateCallbacks cb) {
-            enforceCallerPermission(Manifest.permission.ACCESS_VR_MANAGER);
+            enforceCallerPermissionAnyOf(Manifest.permission.ACCESS_VR_MANAGER,
+                    Manifest.permission.ACCESS_VR_STATE);
             if (cb == null) {
                 throw new IllegalArgumentException("Callback binder object is null.");
             }
@@ -444,7 +448,8 @@
 
         @Override
         public void unregisterPersistentVrStateListener(IPersistentVrStateCallbacks cb) {
-            enforceCallerPermission(Manifest.permission.ACCESS_VR_MANAGER);
+            enforceCallerPermissionAnyOf(Manifest.permission.ACCESS_VR_MANAGER,
+                    Manifest.permission.ACCESS_VR_STATE);
             if (cb == null) {
                 throw new IllegalArgumentException("Callback binder object is null.");
             }
@@ -454,19 +459,28 @@
 
         @Override
         public boolean getVrModeState() {
+            enforceCallerPermissionAnyOf(Manifest.permission.ACCESS_VR_MANAGER,
+                    Manifest.permission.ACCESS_VR_STATE);
             return VrManagerService.this.getVrMode();
         }
 
         @Override
+        public boolean getPersistentVrModeEnabled() {
+            enforceCallerPermissionAnyOf(Manifest.permission.ACCESS_VR_MANAGER,
+                    Manifest.permission.ACCESS_VR_STATE);
+            return VrManagerService.this.getPersistentVrMode();
+        }
+
+        @Override
         public void setPersistentVrModeEnabled(boolean enabled) {
-            enforceCallerPermission(Manifest.permission.RESTRICTED_VR_ACCESS);
+            enforceCallerPermissionAnyOf(Manifest.permission.RESTRICTED_VR_ACCESS);
             VrManagerService.this.setPersistentVrModeEnabled(enabled);
         }
 
         @Override
         public void setVr2dDisplayProperties(
                 Vr2dDisplayProperties vr2dDisplayProp) {
-            enforceCallerPermission(Manifest.permission.RESTRICTED_VR_ACCESS);
+            enforceCallerPermissionAnyOf(Manifest.permission.RESTRICTED_VR_ACCESS);
             VrManagerService.this.setVr2dDisplayProperties(vr2dDisplayProp);
         }
 
@@ -530,11 +544,21 @@
 
     };
 
-    private void enforceCallerPermission(String permission) {
-        if (mContext.checkCallingOrSelfPermission(permission)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Caller does not hold the permission " + permission);
+    /**
+     * Enforces that at lease one of the specified permissions is held by the caller.
+     * Throws SecurityException if none of the specified permissions are held.
+     *
+     * @param permissions One or more permissions to check against.
+     */
+    private void enforceCallerPermissionAnyOf(String... permissions) {
+        for (String permission : permissions) {
+            if (mContext.checkCallingOrSelfPermission(permission)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return;
+            }
         }
+        throw new SecurityException("Caller does not hold at least one of the permissions: "
+                + Arrays.toString(permissions));
     }
 
     /**
@@ -1204,4 +1228,10 @@
             return mVrModeEnabled;
         }
     }
+
+    private boolean getPersistentVrMode() {
+        synchronized (mLock) {
+            return mPersistentVrModeEnabled;
+        }
+    }
 }
diff --git a/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp b/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
index 87312f8..f9cbd16 100644
--- a/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
+++ b/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
@@ -74,10 +74,9 @@
 hidl_handle handleFromFileDescriptor(base::unique_fd fd) {
     hidl_handle h;
 
-    NATIVE_HANDLE_DECLARE_STORAGE(storage, 0, 0);
     static constexpr int kNumFds = 1;
     static constexpr int kNumInts = 0;
-    native_handle_t *nh = native_handle_init(storage, kNumFds, kNumInts);
+    native_handle_t *nh = native_handle_create(kNumFds, kNumInts);
     nh->data[0] = fd.release();
 
     static constexpr bool kTakeOwnership = true;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3c1262f..221b9b4 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -828,9 +828,11 @@
             // because it need to connect to SensorManager. This have to start
             // after START_SENSOR_SERVICE is done.
             SystemServerInitThreadPool.get().submit(() -> {
-                traceBeginAndSlog(START_HIDL_SERVICES);
+                TimingsTraceLog traceLog = new TimingsTraceLog(
+                        SYSTEM_SERVER_TIMING_ASYNC_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+                traceLog.traceBegin(START_HIDL_SERVICES);
                 startHidlServices();
-                traceEnd();
+                traceLog.traceEnd();
             }, START_HIDL_SERVICES);
 
             if (!disableVrManager) {
diff --git a/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
index 8242149..613f01c 100644
--- a/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
@@ -91,6 +91,7 @@
     private ArrayMap<Integer, String> mExpectedSecondaryPackages;
     private ArrayMap<Integer, String> mExpectedSecondaryComponentNames;
 
+    // type : user : list of approved
     private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedPrimary = new ArrayMap<>();
     private ArrayMap<Integer, ArrayMap<Integer, String>> mExpectedSecondary = new ArrayMap<>();
 
@@ -578,6 +579,32 @@
         assertEquals(0, service.getAllowedComponents(10).size());
     }
 
+    @Test
+    public void testOnUserRemoved() throws Exception {
+        for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
+            ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                    mIpm, approvalLevel);
+            loadXml(service);
+
+            ArrayMap<Integer, String> verifyMap = mExpectedPrimary.get(service.mApprovalLevel);
+            String user0 = verifyMap.remove(0);
+            verifyMap = mExpectedSecondary.get(service.mApprovalLevel);
+            user0 = user0 + ":" + verifyMap.remove(0);
+
+            service.onUserRemoved(0);
+
+            for (String verifyValue : user0.split(":")) {
+                if (!TextUtils.isEmpty(verifyValue)) {
+                    assertFalse("service type " + service.mApprovalLevel + ":" + verifyValue
+                            + " is still allowed",
+                            service.isPackageOrComponentAllowed(verifyValue, 0));
+                }
+            }
+
+            verifyExpectedApprovedEntries(service);
+        }
+    }
+
     private void loadXml(ManagedServices service) throws Exception {
         final StringBuffer xml = new StringBuffer();
         xml.append("<" + service.getConfig().xmlTag + ">\n");
@@ -657,7 +684,8 @@
             for (String packageOrComponent : verifyMap.get(userId).split(":")) {
                 if (!TextUtils.isEmpty(packageOrComponent)) {
                     if (service.mApprovalLevel == APPROVAL_BY_PACKAGE) {
-                        assertTrue(packageOrComponent, service.isComponentEnabledForPackage(packageOrComponent));
+                        assertTrue(packageOrComponent,
+                                service.isComponentEnabledForPackage(packageOrComponent));
                         for (int i = 1; i <= 3; i ++) {
                             ComponentName componentName = ComponentName.unflattenFromString(
                                     packageOrComponent +"/C" + i);
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index 0dbf4ee..bf56ec0 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -42,7 +42,7 @@
     SDK_NOUGAT = 24,
     SDK_NOUGAT_MR1 = 25,
     SDK_O = 26,
-    SDK_O_MR1 = 10000, // STOPSHIP Replace with the real version.
+    SDK_O_MR1 = 27,
 };
 
 #endif // H_AAPT_SDK_CONSTANTS
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 864e57d..5c32ed4 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -52,7 +52,7 @@
   SDK_NOUGAT = 24,
   SDK_NOUGAT_MR1 = 25,
   SDK_O = 26,
-  SDK_O_MR1 = 10000, // STOPSHIP Replace with the real version.
+  SDK_O_MR1 = 27,
 };
 
 ApiVersion FindAttributeSdkLevel(const ResourceId& id);
diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
index f61dfdc..b22ae070 100755
--- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
+++ b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
@@ -26,15 +26,14 @@
 import android.os.Handler;
 import android.os.Process;
 import android.util.Log;
+import android.util.LruCache;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * {@link INetworkScoreCache} implementation for Wifi Networks.
@@ -50,18 +49,21 @@
     // scorer to provide an RSSI threshold below which a network should not be used.
     public static final int INVALID_NETWORK_SCORE = Byte.MIN_VALUE;
 
+    /** Default number entries to be stored in the {@link LruCache}. */
+    private static final int DEFAULT_MAX_CACHE_SIZE = 100;
+
     // See {@link #CacheListener}.
     @Nullable
-    @GuardedBy("mCacheLock")
+    @GuardedBy("mLock")
     private CacheListener mListener;
 
     private final Context mContext;
-    private final Object mCacheLock = new Object();
+    private final Object mLock = new Object();
 
     // The key is of the form "<ssid>"<bssid>
     // TODO: What about SSIDs that can't be encoded as UTF-8?
-    private final Map<String, ScoredNetwork> mNetworkCache;
-
+    @GuardedBy("mLock")
+    private final LruCache<String, ScoredNetwork> mCache;
 
     public WifiNetworkScoreCache(Context context) {
         this(context, null /* listener */);
@@ -74,9 +76,14 @@
      * @param listener CacheListener for cache updates
      */
     public WifiNetworkScoreCache(Context context, @Nullable CacheListener listener) {
+        this(context, listener, DEFAULT_MAX_CACHE_SIZE);
+    }
+
+    public WifiNetworkScoreCache(
+            Context context, @Nullable CacheListener listener, int maxCacheSize) {
         mContext = context.getApplicationContext();
         mListener = listener;
-        mNetworkCache = new HashMap<>();
+        mCache = new LruCache<>(maxCacheSize);
     }
 
     @Override public final void updateScores(List<ScoredNetwork> networks) {
@@ -89,7 +96,7 @@
 
         boolean changed = false;
 
-        synchronized(mNetworkCache) {
+        synchronized(mLock) {
             for (ScoredNetwork network : networks) {
                 String networkKey = buildNetworkKey(network);
                 if (networkKey == null) {
@@ -98,12 +105,10 @@
                     }
                     continue;
                 }
-                mNetworkCache.put(networkKey, network);
+                mCache.put(networkKey, network);
                 changed = true;
             }
-        }
 
-        synchronized (mCacheLock) {
             if (mListener != null && changed) {
                 mListener.post(networks);
             }
@@ -111,8 +116,8 @@
     }
 
     @Override public final void clearScores() {
-        synchronized (mNetworkCache) {
-            mNetworkCache.clear();
+        synchronized (mLock) {
+            mCache.evictAll();
         }
     }
 
@@ -138,7 +143,6 @@
     }
 
     public int getNetworkScore(ScanResult result) {
-
         int score = INVALID_NETWORK_SCORE;
 
         ScoredNetwork network = getScoredNetwork(result);
@@ -164,7 +168,6 @@
     }
 
     public int getNetworkScore(ScanResult result, boolean isActiveNetwork) {
-
         int score = INVALID_NETWORK_SCORE;
 
         ScoredNetwork network = getScoredNetwork(result);
@@ -185,8 +188,8 @@
         String key = buildNetworkKey(result);
         if (key == null) return null;
 
-        synchronized(mNetworkCache) {
-            ScoredNetwork network = mNetworkCache.get(key);
+        synchronized(mLock) {
+            ScoredNetwork network = mCache.get(key);
             return network;
         }
     }
@@ -201,8 +204,8 @@
             }
             return null;
         }
-        synchronized (mNetworkCache) {
-            return mNetworkCache.get(key);
+        synchronized (mLock) {
+            return mCache.get(key);
         }
     }
 
@@ -248,33 +251,35 @@
                 mContext.getPackageName(), Process.myUid());
         writer.println(header);
         writer.println("  All score curves:");
-        for (ScoredNetwork score : mNetworkCache.values()) {
-            writer.println("    " + score);
-        }
-        writer.println("  Current network scores:");
-        WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
-        for (ScanResult scanResult : wifiManager.getScanResults()) {
-            writer.println("    " + buildNetworkKey(scanResult) + ": " + getNetworkScore(scanResult));
+        synchronized (mLock) {
+            for (ScoredNetwork score : mCache.snapshot().values()) {
+                writer.println("    " + score);
+            }
+            writer.println("  Network scores for latest ScanResults:");
+            WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+            for (ScanResult scanResult : wifiManager.getScanResults()) {
+                writer.println(
+                        "    " + buildNetworkKey(scanResult) + ": " + getNetworkScore(scanResult));
+            }
         }
     }
 
     /** Registers a CacheListener instance, replacing the previous listener if it existed. */
     public void registerListener(CacheListener listener) {
-        synchronized (mCacheLock) {
+        synchronized (mLock) {
             mListener = listener;
         }
     }
 
     /** Removes the registered CacheListener. */
     public void unregisterListener() {
-        synchronized (mCacheLock) {
+        synchronized (mLock) {
             mListener = null;
         }
     }
 
     /** Listener for updates to the cache inside WifiNetworkScoreCache. */
     public abstract static class CacheListener {
-
         private Handler mHandler;
 
         /**
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
index afab1a3..c98e40a 100644
--- a/wifi/tests/Android.mk
+++ b/wifi/tests/Android.mk
@@ -49,14 +49,15 @@
 LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-	android-support-test \
-	core-test-rules \
-	guava \
-	mockito-target-minus-junit4 \
-	frameworks-base-testutils \
+    android-support-test \
+    core-test-rules \
+    guava \
+    mockito-target-minus-junit4 \
+    frameworks-base-testutils \
+    truth-prebuilt \
 
 LOCAL_JAVA_LIBRARIES := \
-	android.test.runner \
+    android.test.runner \
 
 LOCAL_PACKAGE_NAME := FrameworksWifiApiTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
index 18f6bc8..997282b 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkScoreCacheTest.java
@@ -16,9 +16,9 @@
 
 package android.net.wifi;
 
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -34,12 +34,9 @@
 
 import com.google.common.collect.ImmutableList;
 
-import org.junit.Rule;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -54,7 +51,11 @@
 public class WifiNetworkScoreCacheTest {
 
     public static final String SSID = "ssid";
+    public static final String SSID2 = "ssid2";
+    public static final String SSID3 = "ssid3";
     public static final String FORMATTED_SSID = "\"" + SSID + "\"";
+    public static final String FORMATTED_SSID2 = "\"" + SSID2 + "\"";
+    public static final String FORMATTED_SSID3 = "\"" + SSID3 + "\"";
     public static final String BSSID = "AA:AA:AA:AA:AA:AA";
 
     public static final WifiKey VALID_KEY = new WifiKey(FORMATTED_SSID, BSSID);
@@ -120,13 +121,13 @@
 
     @Test
     public void isScoredNetworkShouldReturnTrueAfterUpdateScoresIsCalled() {
-        assertTrue(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT));
+        assertThat(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT)).isTrue();
     }
 
     @Test
     public void isScoredNetworkShouldReturnFalseAfterClearScoresIsCalled() {
         mScoreCache.clearScores();
-        assertFalse(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT));
+        assertThat(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT)).isFalse();
     }
 
     @Test
@@ -137,19 +138,19 @@
 
         mScoreCache.updateScores(ImmutableList.of(network2));
 
-        assertTrue(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT));
-        assertTrue(mScoreCache.isScoredNetwork(result2));
+        assertThat(mScoreCache.isScoredNetwork(VALID_SCAN_RESULT)).isTrue();
+        assertThat(mScoreCache.isScoredNetwork(result2)).isTrue();
     }
 
     @Test
     public void hasScoreCurveShouldReturnTrue() {
-        assertTrue(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT));
+        assertThat(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT)).isTrue();
     }
 
     @Test
     public void hasScoreCurveShouldReturnFalseWhenNoCachedNetwork() {
         ScanResult unscored = buildScanResult("fake", BSSID);
-        assertFalse(mScoreCache.hasScoreCurve(unscored));
+        assertThat(mScoreCache.hasScoreCurve(unscored)).isFalse();
     }
 
     @Test
@@ -157,7 +158,7 @@
         ScoredNetwork noCurve = buildScoredNetwork(VALID_KEY, null /* rssiCurve */);
         mScoreCache.updateScores(ImmutableList.of(noCurve));
 
-        assertFalse(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT));
+        assertThat(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT)).isFalse();
     }
 
     @Test
@@ -169,12 +170,12 @@
 
         when(mockRssiCurve.lookupScore(rssi)).thenReturn(score);
 
-        assertEquals(score, mScoreCache.getNetworkScore(result));
+        assertThat(mScoreCache.getNetworkScore(result)).isEqualTo(score);
     }
 
     @Test
     public void getMeteredHintShouldReturnFalse() {
-        assertFalse(mScoreCache.getMeteredHint(VALID_SCAN_RESULT));
+        assertThat(mScoreCache.getMeteredHint(VALID_SCAN_RESULT)).isFalse();
     }
 
     @Test
@@ -184,7 +185,7 @@
                     new NetworkKey(VALID_KEY), mockRssiCurve, true /* metered Hint */);
         mScoreCache.updateScores(ImmutableList.of(network));
 
-        assertTrue(mScoreCache.getMeteredHint(VALID_SCAN_RESULT));
+        assertThat(mScoreCache.getMeteredHint(VALID_SCAN_RESULT)).isTrue();
     }
 
     @Test
@@ -197,7 +198,28 @@
         } catch (InterruptedException e) {
             fail("Interrupted Exception while waiting for listener to be invoked.");
         }
-        assertEquals("One network should be updated", 1, mUpdatedNetworksCaptor.size());
-        assertEquals(mValidScoredNetwork, mUpdatedNetworksCaptor.get(0));
+        // One network should be updated.
+        assertThat(mUpdatedNetworksCaptor.size()).isEqualTo(1);
+        assertThat(mUpdatedNetworksCaptor.get(0)).isEqualTo(mValidScoredNetwork);
+    }
+
+    @Test
+    public void leastRecentlyUsedScore_shouldBeEvictedFromCache() {
+        mScoreCache = new WifiNetworkScoreCache(mockContext, mCacheListener, 2 /* maxCacheSize */);
+
+        ScoredNetwork network1 = mValidScoredNetwork;
+        ScoredNetwork network2 = buildScoredNetwork(
+                new WifiKey(FORMATTED_SSID2, BSSID), mockRssiCurve);
+        ScoredNetwork network3 = buildScoredNetwork(
+                new WifiKey(FORMATTED_SSID3, BSSID), mockRssiCurve);
+        mScoreCache.updateScores(ImmutableList.of(network1));
+        mScoreCache.updateScores(ImmutableList.of(network2));
+
+        // First score should be evicted because max cache size has been reached.
+        mScoreCache.updateScores(ImmutableList.of(network3));
+
+        assertThat(mScoreCache.hasScoreCurve(buildScanResult(SSID2, BSSID))).isTrue();
+        assertThat(mScoreCache.hasScoreCurve(buildScanResult(SSID3, BSSID))).isTrue();
+        assertThat(mScoreCache.hasScoreCurve(VALID_SCAN_RESULT)).isFalse();
     }
 }