Merge "DO NOT MERGE - Remove RetailDemoModeService" into oc-dr1-dev
diff --git a/Android.mk b/Android.mk
index cc34767..3b44255 100644
--- a/Android.mk
+++ b/Android.mk
@@ -247,6 +247,7 @@
 	core/java/android/nfc/INfcCardEmulation.aidl \
 	core/java/android/nfc/INfcFCardEmulation.aidl \
 	core/java/android/nfc/INfcUnlockHandler.aidl \
+	core/java/android/nfc/INfcDta.aidl \
 	core/java/android/nfc/ITagRemovedCallback.aidl \
 	core/java/android/os/IBatteryPropertiesListener.aidl \
 	core/java/android/os/IBatteryPropertiesRegistrar.aidl \
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index f991efe..6801618 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -29,6 +29,7 @@
 import android.nfc.INfcFCardEmulation;
 import android.nfc.INfcUnlockHandler;
 import android.nfc.ITagRemovedCallback;
+import android.nfc.INfcDta;
 import android.os.Bundle;
 
 /**
@@ -40,7 +41,7 @@
     INfcCardEmulation getNfcCardEmulationInterface();
     INfcFCardEmulation getNfcFCardEmulationInterface();
     INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg);
-
+    INfcDta getNfcDtaInterface(in String pkg);
     int getState();
     boolean disable(boolean saveState);
     boolean enable();
diff --git a/core/java/android/nfc/INfcDta.aidl b/core/java/android/nfc/INfcDta.aidl
new file mode 100644
index 0000000..4cc5927
--- /dev/null
+++ b/core/java/android/nfc/INfcDta.aidl
@@ -0,0 +1,34 @@
+ /*
+  * Copyright (C) 2017 NXP Semiconductors
+  *
+  * 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.nfc;
+
+import android.os.Bundle;
+
+/**
+ * {@hide}
+ */
+interface INfcDta {
+
+    void enableDta();
+    void disableDta();
+    boolean enableServer(String serviceName, int serviceSap, int miu,
+            int rwSize,int testCaseId);
+    void disableServer();
+    boolean enableClient(String serviceName, int miu, int rwSize,
+            int testCaseId);
+    void disableClient();
+    boolean registerMessageService(String msgServiceName);
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 48869c7..debef63 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -16,8 +16,6 @@
 
 package android.nfc;
 
-import java.util.HashMap;
-
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -43,6 +41,7 @@
 import android.util.Log;
 
 import java.io.IOException;
+import java.util.HashMap;
 
 /**
  * Represents the local NFC adapter.
@@ -627,6 +626,23 @@
     }
 
     /**
+     * Returns the binder interface to the NFC-DTA test interface.
+     * @hide
+     */
+    public INfcDta getNfcDtaInterface() {
+        if (mContext == null) {
+            throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
+                    + " NFC extras APIs");
+        }
+        try {
+            return sService.getNfcDtaInterface(mContext.getPackageName());
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return null;
+        }
+    }
+
+    /**
      * NFC service dead - attempt best effort recovery
      * @hide
      */
diff --git a/core/java/android/nfc/dta/NfcDta.java b/core/java/android/nfc/dta/NfcDta.java
new file mode 100644
index 0000000..8801662
--- /dev/null
+++ b/core/java/android/nfc/dta/NfcDta.java
@@ -0,0 +1,167 @@
+/*
+ * 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.nfc.dta;
+
+import android.content.Context;
+import android.nfc.INfcDta;
+import android.nfc.NfcAdapter;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * This class provides the primary API for DTA operations.
+ * @hide
+ */
+public final class NfcDta {
+    private static final String TAG = "NfcDta";
+
+    private static INfcDta sService;
+    private static HashMap<Context, NfcDta> sNfcDtas = new HashMap<Context, NfcDta>();
+
+    private final Context mContext;
+
+    private NfcDta(Context context, INfcDta service) {
+        mContext = context.getApplicationContext();
+        sService = service;
+    }
+
+    /**
+     * Helper to get an instance of this class.
+     *
+     * @param adapter A reference to an NfcAdapter object.
+     * @return
+     */
+    public static synchronized NfcDta getInstance(NfcAdapter adapter) {
+        if (adapter == null) throw new NullPointerException("NfcAdapter is null");
+        Context context = adapter.getContext();
+        if (context == null) {
+            Log.e(TAG, "NfcAdapter context is null.");
+            throw new UnsupportedOperationException();
+        }
+
+        NfcDta manager = sNfcDtas.get(context);
+        if (manager == null) {
+            INfcDta service = adapter.getNfcDtaInterface();
+            if (service == null) {
+                Log.e(TAG, "This device does not implement the INfcDta interface.");
+                throw new UnsupportedOperationException();
+            }
+            manager = new NfcDta(context, service);
+            sNfcDtas.put(context, manager);
+        }
+        return manager;
+    }
+
+    /**
+     * Enables DTA mode
+     *
+     * @return true/false if enabling was successful
+     */
+    public boolean enableDta() {
+        try {
+            sService.enableDta();
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Disables DTA mode
+     *
+     * @return true/false if disabling was successful
+     */
+    public boolean disableDta() {
+        try {
+            sService.disableDta();
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Enables Server
+     *
+     * @return true/false if enabling was successful
+     */
+    public boolean enableServer(String serviceName, int serviceSap, int miu,
+            int rwSize, int testCaseId) {
+        try {
+            return sService.enableServer(serviceName, serviceSap, miu, rwSize, testCaseId);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Disables Server
+     *
+     * @return true/false if disabling was successful
+     */
+    public boolean disableServer() {
+        try {
+            sService.disableServer();
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Enables Client
+     *
+     * @return true/false if enabling was successful
+     */
+    public boolean enableClient(String serviceName, int miu, int rwSize,
+            int testCaseId) {
+        try {
+            return sService.enableClient(serviceName, miu, rwSize, testCaseId);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Disables client
+     *
+     * @return true/false if disabling was successful
+     */
+    public boolean disableClient() {
+        try {
+            sService.disableClient();
+        } catch (RemoteException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Registers Message Service
+     *
+     * @return true/false if registration was successful
+     */
+    public boolean registerMessageService(String msgServiceName) {
+        try {
+            return sService.registerMessageService(msgServiceName);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+}
diff --git a/core/java/android/service/vr/IVrListener.aidl b/core/java/android/service/vr/IVrListener.aidl
index afb13d3..acca3fa 100644
--- a/core/java/android/service/vr/IVrListener.aidl
+++ b/core/java/android/service/vr/IVrListener.aidl
@@ -20,5 +20,5 @@
 
 /** @hide */
 oneway interface IVrListener {
-    void focusedActivityChanged(in ComponentName component);
+    void focusedActivityChanged(in ComponentName component, boolean running2dInVr, int pid);
 }
diff --git a/core/java/android/service/vr/VrListenerService.java b/core/java/android/service/vr/VrListenerService.java
index 5da4560..fa3d065 100644
--- a/core/java/android/service/vr/VrListenerService.java
+++ b/core/java/android/service/vr/VrListenerService.java
@@ -70,8 +70,10 @@
 
     private final IVrListener.Stub mBinder = new IVrListener.Stub() {
         @Override
-        public void focusedActivityChanged(ComponentName component) {
-            mHandler.obtainMessage(MSG_ON_CURRENT_VR_ACTIVITY_CHANGED, component).sendToTarget();
+        public void focusedActivityChanged(
+                ComponentName component, boolean running2dInVr, int pid) {
+            mHandler.obtainMessage(MSG_ON_CURRENT_VR_ACTIVITY_CHANGED, running2dInVr ? 1 : 0,
+                    pid, component).sendToTarget();
         }
     };
 
@@ -84,7 +86,8 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_ON_CURRENT_VR_ACTIVITY_CHANGED: {
-                    VrListenerService.this.onCurrentVrActivityChanged((ComponentName) msg.obj);
+                    VrListenerService.this.onCurrentVrActivityChanged(
+                            (ComponentName) msg.obj, msg.arg1 == 1, msg.arg2);
                 } break;
             }
         }
@@ -120,6 +123,29 @@
     }
 
     /**
+     * An extended version of onCurrentVrActivityChanged
+     *
+     * <p>This will be called when this service is initially bound, but is not
+     * guaranteed to be called before onUnbind.  In general, this is intended to be used to
+     * determine when user focus has transitioned between two VR activities, or between a
+     * VR activity and a 2D activity. This should be overridden instead of the above
+     * onCurrentVrActivityChanged as that version is deprecated.</p>
+     *
+     * @param component the {@link ComponentName} of the VR activity or the 2D intent.
+     * @param running2dInVr true if the component is a 2D component.
+     * @param pid the process the component is running in.
+     *
+     * @see android.app.Activity#setVrModeEnabled
+     * @see android.R.attr#enableVrMode
+     * @hide
+     */
+    public void onCurrentVrActivityChanged(
+            ComponentName component, boolean running2dInVr, int pid) {
+        // Override to implement. Default to old behaviour of sending null for 2D.
+        onCurrentVrActivityChanged(running2dInVr ? null : component);
+    }
+
+    /**
      * Checks if the given component is enabled in user settings.
      *
      * <p>If this component is not enabled in the user's settings, it will not be started when
diff --git a/core/java/android/util/AtomicFile.java b/core/java/android/util/AtomicFile.java
index 2f1abe9..0122e49 100644
--- a/core/java/android/util/AtomicFile.java
+++ b/core/java/android/util/AtomicFile.java
@@ -202,6 +202,15 @@
     }
 
     /**
+     * @hide
+     * Checks if the original or backup file exists.
+     * @return whether the original or backup file exists.
+     */
+    public boolean exists() {
+        return mBaseName.exists() || mBackupName.exists();
+    }
+
+    /**
      * Gets the last modified time of the atomic file.
      * {@hide}
      *
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 7346a21..263d3ff 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1133,6 +1133,15 @@
     }
 
     /**
+     * Returns true if the display may be in a reduced operating mode while in the
+     * specified display power state.
+     * @hide
+     */
+    public static boolean isDozeState(int state) {
+        return state == STATE_DOZE || state == STATE_DOZE_SUSPEND;
+    }
+
+    /**
      * A mode supported by a given display.
      *
      * @see Display#getSupportedModes()
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 9b881fd..faf3e125 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2591,5 +2591,16 @@
             encoder.addProperty("type", type);
             encoder.addProperty("flags", flags);
         }
+
+        /**
+         * @hide
+         * @return True if the layout parameters will cause the window to cover the full screen;
+         *         false otherwise.
+         */
+        public boolean isFullscreen() {
+            return x == 0 && y == 0
+                    && width == WindowManager.LayoutParams.MATCH_PARENT
+                    && height == WindowManager.LayoutParams.MATCH_PARENT;
+        }
     }
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 60db8ca..f817ab3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1862,6 +1862,11 @@
          states. -->
     <bool name="config_dozeAlwaysOnDisplayAvailable">false</bool>
 
+    <!-- Whether the display hardware requires we go to the off state before transitioning
+         out of any doze states. -->
+    <bool name="config_displayTransitionOffAfterDoze">false</bool>
+
+
     <!-- Power Management: Specifies whether to decouple the auto-suspend state of the
          device from the display on/off state.
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c49763f..7670c7b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3064,6 +3064,7 @@
   <java-symbol type="bool" name="config_handleVolumeKeysInWindowManager" />
   <java-symbol type="integer" name="config_inCallNotificationVolumeRelative" />
   <java-symbol type="bool" name="config_dozeAlwaysOnDisplayAvailable" />
+  <java-symbol type="bool" name="config_displayTransitionOffAfterDoze" />
   <java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
   <java-symbol type="string" name="config_headlineFontFamily" />
   <java-symbol type="string" name="config_headlineFontFamilyLight" />
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index ec3b520..0400e24 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1065,7 +1065,7 @@
                 // are still seen, we will investigate further.
                 update(config); // Notifies the AccessPointListener of the change
             }
-            if (mRssi != info.getRssi()) {
+            if (mRssi != info.getRssi() && info.getRssi() != WifiInfo.INVALID_RSSI) {
                 mRssi = info.getRssi();
                 updated = true;
             } else if (mNetworkInfo != null && networkInfo != null
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 9ccd332..5a35da9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -365,7 +365,7 @@
                 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
                 mRegistered = false;
             }
-            unregisterAndClearScoreCache();
+            unregisterScoreCache();
             pauseScanning();
             mContext.getContentResolver().unregisterContentObserver(mObserver);
 
@@ -375,11 +375,14 @@
         mStaleScanResults = true;
     }
 
-    private void unregisterAndClearScoreCache() {
+    private void unregisterScoreCache() {
         mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, mScoreCache);
-        mScoreCache.clearScores();
 
-        // Synchronize on mLock to avoid concurrent modification during updateAccessPoints
+        // We do not want to clear the existing scores in the cache, as this method is called during
+        // stop tracking on activity pause. Hence, on resumption we want the ability to show the
+        // last known, potentially stale, scores. However, by clearing requested scores, the scores
+        // will be requested again upon resumption of tracking, and if any changes have occurred
+        // the listeners (UI) will be updated accordingly.
         synchronized (mLock) {
             mRequestedScores.clear();
         }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index c08dd6e..2f02b9b 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -626,6 +626,57 @@
     }
 
     @Test
+    public void testUpdateWithDifferentRssi_returnsTrue() {
+        int networkId = 123;
+        int rssi = -55;
+        WifiConfiguration config = new WifiConfiguration();
+        config.networkId = networkId;
+        WifiInfo wifiInfo = new WifiInfo();
+        wifiInfo.setNetworkId(networkId);
+        wifiInfo.setRssi(rssi);
+
+        NetworkInfo networkInfo =
+                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
+        networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTING, "", "");
+
+        AccessPoint ap = new TestAccessPointBuilder(mContext)
+                .setNetworkInfo(networkInfo)
+                .setNetworkId(networkId)
+                .setRssi(rssi)
+                .setWifiInfo(wifiInfo)
+                .build();
+
+        NetworkInfo newInfo = new NetworkInfo(networkInfo); // same values
+        wifiInfo.setRssi(rssi + 1);
+        assertThat(ap.update(config, wifiInfo, newInfo)).isTrue();
+    }
+
+    @Test
+    public void testUpdateWithInvalidRssi_returnsFalse() {
+        int networkId = 123;
+        int rssi = -55;
+        WifiConfiguration config = new WifiConfiguration();
+        config.networkId = networkId;
+        WifiInfo wifiInfo = new WifiInfo();
+        wifiInfo.setNetworkId(networkId);
+        wifiInfo.setRssi(rssi);
+
+        NetworkInfo networkInfo =
+                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
+        networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTING, "", "");
+
+        AccessPoint ap = new TestAccessPointBuilder(mContext)
+                .setNetworkInfo(networkInfo)
+                .setNetworkId(networkId)
+                .setRssi(rssi)
+                .setWifiInfo(wifiInfo)
+                .build();
+
+        NetworkInfo newInfo = new NetworkInfo(networkInfo); // same values
+        wifiInfo.setRssi(WifiInfo.INVALID_RSSI);
+        assertThat(ap.update(config, wifiInfo, newInfo)).isFalse();
+    }
+    @Test
     public void testUpdateWithConfigChangeOnly_returnsFalseButInvokesListener() {
         int networkId = 123;
         int rssi = -55;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index b6d0c45..c87d01a 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -473,6 +473,17 @@
     }
 
     @Test
+    public void stopTracking_shouldNotClearExistingScores()
+            throws InterruptedException {
+        // Start the tracker and inject the initial scan results and then stop tracking
+        WifiTracker tracker =  createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
+        updateScoresAndWaitForAccessPointsChangedCallback(tracker);
+        tracker.stopTracking();
+
+        assertThat(mScoreCacheCaptor.getValue().getScoredNetwork(NETWORK_KEY_1)).isNotNull();
+    }
+
+    @Test
     public void scoreCacheUpdateScoresShouldTriggerOnAccessPointsChanged()
             throws InterruptedException {
         WifiTracker tracker = createMockedWifiTracker();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index f5d7dd8..ad2ec0b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2597,7 +2597,7 @@
             synchronized (mLock) {
                 final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
                 File globalFile = getSettingsFile(key);
-                if (globalFile.exists()) {
+                if (SettingsState.stateFileExists(globalFile)) {
                     return;
                 }
 
@@ -2634,7 +2634,7 @@
             // Every user has secure settings and if no file we need to migrate.
             final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId);
             File secureFile = getSettingsFile(secureKey);
-            if (secureFile.exists()) {
+            if (SettingsState.stateFileExists(secureFile)) {
                 return;
             }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 5f4b239..d3ac11a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -689,17 +689,11 @@
 
     private void readStateSyncLocked() {
         FileInputStream in;
-        if (!mStatePersistFile.exists()) {
-            Slog.i(LOG_TAG, "No settings state " + mStatePersistFile);
-            addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
-            return;
-        }
         try {
             in = new AtomicFile(mStatePersistFile).openRead();
         } catch (FileNotFoundException fnfe) {
-            String message = "No settings state " + mStatePersistFile;
-            Slog.wtf(LOG_TAG, message);
-            Slog.i(LOG_TAG, message);
+            Slog.i(LOG_TAG, "No settings state " + mStatePersistFile);
+            addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
             return;
         }
         try {
@@ -715,6 +709,16 @@
         }
     }
 
+    /**
+     * Uses AtomicFile to check if the file or its backup exists.
+     * @param file The file to check for existence
+     * @return whether the original or backup exist
+     */
+    public static boolean stateFileExists(File file) {
+        AtomicFile stateFile = new AtomicFile(file);
+        return stateFile.exists();
+    }
+
     private void parseStateLocked(XmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 1705f79..58117bd 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -273,6 +273,9 @@
     <!-- Doze: the brightness value to use for the higher brightness AOD mode -->
     <integer name="config_doze_aod_brightness_high">27</integer>
 
+    <!-- Doze: the brightness value to use for the sunlight AOD mode -->
+    <integer name="config_doze_aod_brightness_sunlight">28</integer>
+
     <!-- Doze: whether the double tap sensor reports 2D touch coordinates -->
     <bool name="doze_double_tap_reports_touch_coordinates">false</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index fcbe3e9..94687de 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -784,6 +784,9 @@
     <!-- The shortest-edge size of the expanded PiP. -->
     <dimen name="pip_expanded_shortest_edge_size">160dp</dimen>
 
+    <!-- The additional offset to apply to the IME animation to account for the input field. -->
+    <dimen name="pip_ime_offset">48dp</dimen>
+
     <!-- The padding between actions in the PiP in landscape  Note that the PiP does not reflect
          the configuration of the device, so we can't use -land resources. -->
     <dimen name="pip_between_action_padding_land">8dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index 44cf003..fe8373f 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -98,7 +98,7 @@
      */
     @Override
     public GradientColors getColors(int which) {
-        return getColors(which, TYPE_NORMAL);
+        return getColors(which, TYPE_DARK);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 0be4eda..a1dfeb3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -91,6 +91,7 @@
                 case UNINITIALIZED:
                 case INITIALIZED:
                 case DOZE:
+                case DOZE_REQUEST_PULSE:
                 case DOZE_AOD_PAUSED:
                     return Display.STATE_OFF;
                 case DOZE_PULSING:
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 28a45aa..ed4b131 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -39,6 +39,7 @@
 
     private final int mHighBrightness;
     private final int mLowBrightness;
+    private final int mSunlightBrightness;
 
     public DozeScreenBrightness(Context context, DozeMachine.Service service,
             SensorManager sensorManager, Sensor lightSensor, Handler handler) {
@@ -52,6 +53,8 @@
                 R.integer.config_doze_aod_brightness_low);
         mHighBrightness = context.getResources().getInteger(
                 R.integer.config_doze_aod_brightness_high);
+        mSunlightBrightness = context.getResources().getInteger(
+                R.integer.config_doze_aod_brightness_sunlight);
     }
 
     @Override
@@ -83,9 +86,12 @@
     }
 
     private int computeBrightness(int sensorValue) {
-        // The sensor reports 0 for off, 1 for low brightness and 2 for high brightness.
-        // We currently use DozeScreenState for screen off, so we treat off as low brightness.
-        if (sensorValue >= 2) {
+        // The sensor reports 0 for off, 1 for low brightness, 2 for high brightness, and 3 for
+        // sunlight. We currently use DozeScreenState for screen off, so we treat off as low
+        // brightness.
+        if (sensorValue >= 3) {
+            return mSunlightBrightness;
+        } else if (sensorValue == 2) {
             return mHighBrightness;
         } else {
             return mLowBrightness;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 9b113d8..ad177a7 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -25,8 +25,10 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.HardwareUiLayout;
+import com.android.systemui.Interpolators;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
+import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.volume.VolumeDialogMotion.LogAccelerateInterpolator;
 import com.android.systemui.volume.VolumeDialogMotion.LogDecelerateInterpolator;
@@ -65,6 +67,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.MathUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -112,6 +115,8 @@
     private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
     private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
 
+    private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f;
+
     private final Context mContext;
     private final GlobalActionsManager mWindowManagerFuncs;
     private final AudioManager mAudioManager;
@@ -1291,7 +1296,7 @@
                     .alpha(1)
                     .translationX(0)
                     .setDuration(300)
-                    .setInterpolator(new LogDecelerateInterpolator())
+                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                     .setUpdateListener(animation -> {
                         int alpha = (int) ((Float) animation.getAnimatedValue()
                                 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
@@ -1329,9 +1334,8 @@
                     .setInterpolator(new LogAccelerateInterpolator())
                     .setUpdateListener(animation -> {
                         float frac = animation.getAnimatedFraction();
-                        float alpha = frac *(ScrimController.GRADIENT_SCRIM_ALPHA_BUSY
-                                        - ScrimController.GRADIENT_SCRIM_ALPHA)
-                                + ScrimController.GRADIENT_SCRIM_ALPHA;
+                        float alpha = NotificationUtils.interpolate(
+                                ScrimController.GRADIENT_SCRIM_ALPHA, SHUTDOWN_SCRIM_ALPHA, frac);
                         mGradientDrawable.setAlpha((int) (alpha * 255));
                     })
                     .start();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 6d10d94..e23875f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -811,6 +811,7 @@
         synchronized (this) {
             mDeviceInteractive = false;
             mGoingToSleep = false;
+            mWakeAndUnlocking = false;
 
             resetKeyguardDonePendingLocked();
             mHideAnimationRun = false;
@@ -1957,7 +1958,6 @@
             if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOff");
             mStatusBarKeyguardViewManager.onScreenTurnedOff();
             mDrawnCallback = null;
-            mWakeAndUnlocking = false;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 278fdc3..d3be19d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -27,6 +27,7 @@
 import android.app.IActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -122,6 +123,7 @@
     private boolean mIsMinimized;
     private boolean mIsImeShowing;
     private int mImeHeight;
+    private int mImeOffset;
     private float mSavedSnapFraction = -1f;
     private boolean mSendingHoverAccessibilityEvents;
     private boolean mMovementWithinMinimize;
@@ -192,8 +194,11 @@
         };
         mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mMenuController,
                 mSnapAlgorithm, mFlingAnimationUtils);
-        mExpandedShortestEdgeSize = context.getResources().getDimensionPixelSize(
+
+        Resources res = context.getResources();
+        mExpandedShortestEdgeSize = res.getDimensionPixelSize(
                 R.dimen.pip_expanded_shortest_edge_size);
+        mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
 
         // Register the listener for input consumer touch events
         inputConsumerController.setTouchListener(this::handleTouchEvent);
@@ -265,7 +270,6 @@
         mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
                 mIsImeShowing ? mImeHeight : 0);
 
-
         // If this is from an IME adjustment, then we should move the PiP so that it is not occluded
         // by the IME
         if (fromImeAdjustement) {
@@ -278,18 +282,22 @@
                         ? expandedMovementBounds
                         : normalMovementBounds;
                 if (mIsImeShowing) {
-                    // IME visible
+                    // IME visible, apply the IME offset if the space allows for it
+                    final int imeOffset = toMovementBounds.bottom - Math.max(toMovementBounds.top,
+                            toMovementBounds.bottom - mImeOffset);
                     if (bounds.top == mMovementBounds.bottom) {
                         // If the PIP is currently resting on top of the IME, then adjust it with
-                        // the hiding IME
-                        bounds.offsetTo(bounds.left, toMovementBounds.bottom);
+                        // the showing IME
+                        bounds.offsetTo(bounds.left, toMovementBounds.bottom - imeOffset);
                     } else {
-                        bounds.offset(0, Math.min(0, toMovementBounds.bottom - bounds.top));
+                        bounds.offset(0, Math.min(0, toMovementBounds.bottom - imeOffset
+                                - bounds.top));
                     }
                 } else {
                     // IME hidden
-                    if (bounds.top == mMovementBounds.bottom) {
-                        // If the PIP is resting on top of the IME, then adjust it with the hiding IME
+                    if (bounds.top >= (mMovementBounds.bottom - mImeOffset)) {
+                        // If the PIP is resting on top of the IME, then adjust it with the hiding
+                        // IME
                         bounds.offsetTo(bounds.left, toMovementBounds.bottom);
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index f591524..2dc467f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -114,6 +114,7 @@
         // be invoked when we're done so that the caller can drop the pulse wakelock.
         mPulseCallback = callback;
         mPulseReason = reason;
+        mScrimController.setDozeInFrontAlpha(1f);
         mHandler.post(mPulseIn);
     }
 
@@ -290,10 +291,6 @@
 
             // Signal that the pulse is ready to turn the screen on and draw.
             pulseStarted();
-
-            if (mDozeParameters.getAlwaysOn()) {
-                mHandler.post(DozeScrimController.this::onScreenTurnedOn);
-            }
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 5af80f5..df059e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -285,7 +285,7 @@
         if (!mUpdateMonitor.isDeviceInteractive()) {
             if (!mStatusBarKeyguardViewManager.isShowing()) {
                 return MODE_ONLY_WAKE;
-            } else if (pulsingOrAod() && unlockingAllowed) {
+            } else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
                 return MODE_WAKE_AND_UNLOCK_PULSING;
             } else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) {
                 return MODE_WAKE_AND_UNLOCK;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index a8b1c91..62d4b73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -25,7 +25,6 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.util.Log;
 import android.util.MathUtils;
 import android.view.View;
 import android.view.ViewGroup;
@@ -120,6 +119,12 @@
     private boolean mKeyguardFadingOutInProgress;
     private boolean mAnimatingDozeUnlock;
     private ValueAnimator mKeyguardFadeoutAnimation;
+    /** Wake up from AOD transition is starting; need fully opaque front scrim */
+    private boolean mWakingUpFromAodStarting;
+    /** Wake up from AOD transition is in progress; need black tint */
+    private boolean mWakingUpFromAodInProgress;
+    /** Wake up from AOD transition is animating; need to reset when animation finishes */
+    private boolean mWakingUpFromAodAnimationRunning;
 
     public ScrimController(LightBarController lightBarController, ScrimView scrimBehind,
             ScrimView scrimInFront, View headsUpScrim) {
@@ -187,9 +192,32 @@
         scheduleUpdate();
     }
 
+    public void prepareWakeUpFromAod() {
+        mWakingUpFromAodInProgress = true;
+        mWakingUpFromAodStarting = true;
+        mAnimateChange = false;
+        scheduleUpdate();
+        onPreDraw();
+    }
+
+    public void wakeUpFromAod() {
+        if (mWakeAndUnlocking || mAnimateKeyguardFadingOut) {
+            // Wake and unlocking has a separate transition that must not be interfered with.
+            mWakingUpFromAodStarting = false;
+            return;
+        }
+        if (mWakingUpFromAodStarting) {
+            mWakingUpFromAodInProgress = true;
+            mWakingUpFromAodStarting = false;
+            mAnimateChange = true;
+            scheduleUpdate();
+        }
+    }
+
     public void setWakeAndUnlocking() {
         mWakeAndUnlocking = true;
         mAnimatingDozeUnlock = true;
+        mWakingUpFromAodStarting = false;
         scheduleUpdate();
     }
 
@@ -356,7 +384,11 @@
             setScrimBehindAlpha(mScrimBehindAlpha);
         } else {
             float fraction = Math.max(0, Math.min(mFraction, 1));
-            setScrimInFrontAlpha(0f);
+            if (mWakingUpFromAodStarting) {
+                setScrimInFrontAlpha(1f);
+            } else {
+                setScrimInFrontAlpha(0f);
+            }
             setScrimBehindAlpha(fraction
                     * (mScrimBehindAlphaKeyguard - mScrimBehindAlphaUnlocking)
                     + mScrimBehindAlphaUnlocking);
@@ -426,7 +458,10 @@
             scrimView.setViewAlpha(alpha);
 
             int dozeTint = Color.TRANSPARENT;
-            if (mAnimatingDozeUnlock || mDozing) {
+
+            boolean dozing = mAnimatingDozeUnlock || mDozing;
+            boolean frontScrimDozing = mWakingUpFromAodInProgress;
+            if (dozing || frontScrimDozing && scrim == mScrimInFront) {
                 dozeTint = Color.BLACK;
             }
             scrimView.setTint(dozeTint);
@@ -458,6 +493,10 @@
                     mKeyguardFadingOutInProgress = false;
                     mAnimatingDozeUnlock = false;
                 }
+                if (mWakingUpFromAodAnimationRunning) {
+                    mWakingUpFromAodAnimationRunning = false;
+                    mWakingUpFromAodInProgress = false;
+                }
                 scrim.setTag(TAG_KEY_ANIM, null);
                 scrim.setTag(TAG_KEY_ANIM_TARGET, null);
             }
@@ -467,6 +506,9 @@
             mKeyguardFadingOutInProgress = true;
             mKeyguardFadeoutAnimation = anim;
         }
+        if (mWakingUpFromAodInProgress) {
+            mWakingUpFromAodAnimationRunning = true;
+        }
         if (mSkipFirstFrame) {
             anim.setCurrentPlayTime(16);
         }
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 e90d1c1..9e1a2a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -21,6 +21,9 @@
 import static android.app.StatusBarManager.windowStateToString;
 import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
 
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
 import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -4208,13 +4211,16 @@
     }
 
     private boolean updateIsKeyguard() {
+        boolean wakeAndUnlocking = mFingerprintUnlockController.getMode()
+                == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+
         // For dozing, keyguard needs to be shown whenever the device is non-interactive. Otherwise
         // there's no surface we can show to the user. Note that the device goes fully interactive
         // late in the transition, so we also allow the device to start dozing once the screen has
         // turned off fully.
         boolean keyguardForDozing = mDozingRequested &&
                 (!mDeviceInteractive || isGoingToSleep() && (isScreenFullyOff() || mIsKeyguard));
-        boolean shouldBeKeyguard = mKeyguardRequested || keyguardForDozing;
+        boolean shouldBeKeyguard = (mKeyguardRequested || keyguardForDozing) && !wakeAndUnlocking;
         if (keyguardForDozing) {
             updatePanelExpansionForKeyguard();
         }
@@ -4256,7 +4262,8 @@
     }
 
     private void updatePanelExpansionForKeyguard() {
-        if (mState == StatusBarState.KEYGUARD) {
+        if (mState == StatusBarState.KEYGUARD && mFingerprintUnlockController.getMode()
+                != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
             instantExpandNotificationsPanel();
         } else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
             instantCollapseNotificationPanel();
@@ -5156,6 +5163,13 @@
         public void onScreenTurningOn() {
             mFalsingManager.onScreenTurningOn();
             mNotificationPanel.onScreenTurningOn();
+
+            int wakefulness = mWakefulnessLifecycle.getWakefulness();
+            if (mDozing && (wakefulness == WAKEFULNESS_WAKING
+                    || wakefulness == WAKEFULNESS_ASLEEP) && !isPulsing()) {
+                mScrimController.prepareWakeUpFromAod();
+            }
+
             if (mLaunchCameraOnScreenTurningOn) {
                 mNotificationPanel.launchCamera(false, mLastCameraLaunchSource);
                 mLaunchCameraOnScreenTurningOn = false;
@@ -5164,13 +5178,18 @@
 
         @Override
         public void onScreenTurnedOn() {
+            mScrimController.wakeUpFromAod();
             mDozeScrimController.onScreenTurnedOn();
         }
 
         @Override
         public void onScreenTurnedOff() {
             mFalsingManager.onScreenOff();
-            updateIsKeyguard();
+            // If we pulse in from AOD, we turn the screen off first. However, updatingIsKeyguard
+            // in that case destroys the HeadsUpManager state, so don't do it in that case.
+            if (!isPulsing()) {
+                updateIsKeyguard();
+            }
         }
     };
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index 203876b..e54c792 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -86,13 +86,13 @@
     }
 
     @Test
-    public void testScreen_onInRequestPulseWithAoD() {
+    public void testScreen_offInRequestPulseWithAoD() {
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
 
         mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE);
 
-        assertEquals(Display.STATE_DOZE_SUSPEND, mServiceFake.screenState);
+        assertEquals(Display.STATE_OFF, mServiceFake.screenState);
     }
 
 }
\ No newline at end of file
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index abfc31e..a27a77e 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -123,8 +123,8 @@
 import com.android.server.SystemConfig;
 import com.android.server.SystemService;
 import com.android.server.backup.PackageManagerBackupAgent.Metadata;
-
 import com.android.server.power.BatterySaverPolicy.ServiceType;
+
 import libcore.io.IoUtils;
 
 import java.io.BufferedInputStream;
@@ -691,6 +691,7 @@
     final SparseArray<Operation> mCurrentOperations = new SparseArray<Operation>();
     final Object mCurrentOpLock = new Object();
     final Random mTokenGenerator = new Random();
+    final AtomicInteger mNextToken = new AtomicInteger();
 
     final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<AdbParams>();
 
@@ -763,15 +764,13 @@
     @GuardedBy("mQueueLock")
     ArrayList<FullBackupEntry> mFullBackupQueue;
 
-    // Utility: build a new random integer token
+    // Utility: build a new random integer token.  The low bits are the ordinal of the
+    // operation for near-time uniqueness, and the upper bits are random for app-
+    // side unpredictability.
     @Override
     public int generateRandomIntegerToken() {
-        int token;
-        do {
-            synchronized (mTokenGenerator) {
-                token = mTokenGenerator.nextInt();
-            }
-        } while (token < 0);
+        int token = mTokenGenerator.nextInt() & ~0xFF;
+        token |= (mNextToken.incrementAndGet() & 0xFF);
         return token;
     }
 
diff --git a/services/core/java/com/android/server/am/VrController.java b/services/core/java/com/android/server/am/VrController.java
index 048bef7..feddfe3 100644
--- a/services/core/java/com/android/server/am/VrController.java
+++ b/services/core/java/com/android/server/am/VrController.java
@@ -163,6 +163,7 @@
         ComponentName requestedPackage;
         ComponentName callingPackage;
         int userId;
+        int processId = -1;
         boolean changed = false;
         synchronized (mGlobalAmLock) {
             vrMode = record.requestedVrComponent != null;
@@ -172,11 +173,15 @@
 
             // Tell the VrController that a VR mode change is requested.
             changed = changeVrModeLocked(vrMode, record.app);
+
+            if (record.app != null) {
+                processId = record.app.pid;
+            }
         }
 
         // Tell VrManager that a VR mode changed is requested, VrManager will handle
         // notifying all non-AM dependencies if needed.
-        vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
+        vrService.setVrMode(vrMode, requestedPackage, userId, processId, callingPackage);
         return changed;
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index e82724d..cbd02ac 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -165,6 +165,12 @@
     // a stylish color fade animation instead.
     private boolean mColorFadeFadesConfig;
 
+    // True if we need to transition to the off state when coming out of a doze state.
+    // Some display hardware will show artifacts (flickers, etc) when transitioning from a doze
+    // to a fully on state. In order to hide these, we first transition to off to let the system
+    // animate the screen on as it normally would, which is a much smoother experience.
+    private boolean mTransitionOffAfterDozeConfig;
+
     // The pending power request.
     // Initially null until the first call to requestPowerState.
     // Guarded by mLock.
@@ -410,6 +416,9 @@
         mColorFadeFadesConfig = resources.getBoolean(
                 com.android.internal.R.bool.config_animateScreenLights);
 
+        mTransitionOffAfterDozeConfig = resources.getBoolean(
+                com.android.internal.R.bool.config_displayTransitionOffAfterDoze);
+
         if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
             mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
             if (mProximitySensor != null) {
@@ -877,6 +886,10 @@
     }
 
     private boolean setScreenState(int state) {
+        return setScreenState(state, false /*force*/);
+    }
+
+    private boolean setScreenState(int state, boolean force) {
         final boolean isOff = (state == Display.STATE_OFF);
         if (mPowerState.getScreenState() != state) {
 
@@ -887,9 +900,17 @@
                     mReportedScreenStateToPolicy = REPORTED_TO_POLICY_SCREEN_TURNING_OFF;
                     blockScreenOff();
                     mWindowManagerPolicy.screenTurningOff(mPendingScreenOffUnblocker);
-                    return false;
+                    if (force) {
+                        // If we're forcing the power state transition then immediately
+                        // unblock the screen off event. This keeps the lifecycle consistent,
+                        // so WindowManagerPolicy will always see screenTurningOff before
+                        // screenTurnedOff, but we don't actually block on them for the state
+                        // change.
+                        unblockScreenOff();
+                    } else {
+                        return false;
+                    }
                 } else if (mPendingScreenOffUnblocker != null) {
-
                     // Abort doing the state change until screen off is unblocked.
                     return false;
                 }
@@ -968,6 +989,17 @@
             mPendingScreenOff = false;
         }
 
+        if (mTransitionOffAfterDozeConfig &&
+                Display.isDozeState(mPowerState.getScreenState())
+                && !Display.isDozeState(target)) {
+            setScreenState(Display.STATE_OFF, true /*force*/);
+            // Skip the screen off animation and add a black surface to hide the
+            // contents of the screen. This will also trigger another power state update so that we
+            // end up converging on the target state.
+            mColorFadeOffAnimator.end();
+            return;
+        }
+
         // If we were in the process of turning off the screen but didn't quite
         // finish.  Then finish up now to prevent a jarring transition back
         // to screen on if we skipped blocking screen on as usual.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 8e2097a..d36d2f1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5417,7 +5417,7 @@
             // represent should be hidden or if we should hide the lockscreen. For attached app
             // windows we defer the decision to the window it is attached to.
             if (appWindow && attached == null) {
-                if (isFullscreen(attrs) && StackId.normallyFullscreenWindows(stackId)) {
+                if (attrs.isFullscreen() && StackId.normallyFullscreenWindows(stackId)) {
                     if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
                     mTopFullscreenOpaqueWindowState = win;
                     if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
@@ -5456,7 +5456,7 @@
         // separately, because both the "real fullscreen" opaque window and the one for the docked
         // stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
         if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null
-                && isFullscreen(attrs) && stackId == DOCKED_STACK_ID) {
+                && attrs.isFullscreen() && stackId == DOCKED_STACK_ID) {
             mTopDockedOpaqueWindowState = win;
             if (mTopDockedOpaqueOrDimmingWindowState == null) {
                 mTopDockedOpaqueOrDimmingWindowState = win;
@@ -5481,12 +5481,6 @@
         }
     }
 
-    private boolean isFullscreen(WindowManager.LayoutParams attrs) {
-        return attrs.x == 0 && attrs.y == 0
-                && attrs.width == WindowManager.LayoutParams.MATCH_PARENT
-                && attrs.height == WindowManager.LayoutParams.MATCH_PARENT;
-    }
-
     /** {@inheritDoc} */
     @Override
     public int finishPostLayoutPolicyLw() {
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 1f75640..bdd9de0 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -52,10 +52,11 @@
      * @param enabled {@code true} to enable VR mode.
      * @param packageName The package name of the requested VrListenerService to bind.
      * @param userId the user requesting the VrListenerService component.
+     * @param processId the process the component is running in.
      * @param calling the component currently using VR mode, or null to leave unchanged.
      */
     public abstract void setVrMode(boolean enabled, @NonNull ComponentName packageName,
-            int userId, @NonNull ComponentName calling);
+            int userId, int processId, @NonNull ComponentName calling);
 
     /**
      * Set whether the system has acquired a sleep token.
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index f13cc76..e737328 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -125,6 +125,8 @@
     private boolean mVrModeAllowed;
     private boolean mVrModeEnabled;
     private boolean mPersistentVrModeEnabled;
+    private boolean mRunning2dInVr;
+    private int mVrAppProcessId;
     private EnabledComponentsObserver mComponentObserver;
     private ManagedApplicationService mCurrentVrService;
     private ComponentName mDefaultVrService;
@@ -174,7 +176,7 @@
                 }
                 consumeAndApplyPendingStateLocked();
                 if (mBootsToVr && !mVrModeEnabled) {
-                  setVrMode(true, mDefaultVrService, 0, null);
+                  setVrMode(true, mDefaultVrService, 0, -1, null);
                 }
             } else {
                 // Disable persistent mode when VR mode isn't allowed, allows an escape hatch to
@@ -183,12 +185,12 @@
 
                 // Set pending state to current state.
                 mPendingState = (mVrModeEnabled && mCurrentVrService != null)
-                    ? new VrState(mVrModeEnabled, mCurrentVrService.getComponent(),
-                        mCurrentVrService.getUserId(), mCurrentVrModeComponent)
+                    ? new VrState(mVrModeEnabled, mRunning2dInVr, mCurrentVrService.getComponent(),
+                        mCurrentVrService.getUserId(), mVrAppProcessId, mCurrentVrModeComponent)
                     : null;
 
                 // Unbind current VR service and do necessary callbacks.
-                updateCurrentVrServiceLocked(false, null, 0, null);
+                updateCurrentVrServiceLocked(false, false, null, 0, -1, null);
             }
         }
     }
@@ -270,26 +272,33 @@
 
     private static class VrState {
         final boolean enabled;
+        final boolean running2dInVr;
         final int userId;
+        final int processId;
         final ComponentName targetPackageName;
         final ComponentName callingPackage;
         final long timestamp;
         final boolean defaultPermissionsGranted;
 
-        VrState(boolean enabled, ComponentName targetPackageName, int userId,
-                ComponentName callingPackage) {
+
+        VrState(boolean enabled, boolean running2dInVr, ComponentName targetPackageName, int userId,
+                int processId, ComponentName callingPackage) {
             this.enabled = enabled;
+            this.running2dInVr = running2dInVr;
             this.userId = userId;
+            this.processId = processId;
             this.targetPackageName = targetPackageName;
             this.callingPackage = callingPackage;
             this.defaultPermissionsGranted = false;
             this.timestamp = System.currentTimeMillis();
         }
 
-        VrState(boolean enabled, ComponentName targetPackageName, int userId,
-            ComponentName callingPackage, boolean defaultPermissionsGranted) {
+        VrState(boolean enabled, boolean running2dInVr, ComponentName targetPackageName, int userId,
+            int processId, ComponentName callingPackage, boolean defaultPermissionsGranted) {
             this.enabled = enabled;
+            this.running2dInVr = running2dInVr;
             this.userId = userId;
+            this.processId = processId;
             this.targetPackageName = targetPackageName;
             this.callingPackage = callingPackage;
             this.defaultPermissionsGranted = defaultPermissionsGranted;
@@ -390,8 +399,9 @@
             }
 
             // There is an active service, update it if needed
-            updateCurrentVrServiceLocked(mVrModeEnabled, mCurrentVrService.getComponent(),
-                    mCurrentVrService.getUserId(), mCurrentVrModeComponent);
+            updateCurrentVrServiceLocked(mVrModeEnabled, mRunning2dInVr,
+                    mCurrentVrService.getComponent(), mCurrentVrService.getUserId(),
+                    mVrAppProcessId, mCurrentVrModeComponent);
         }
     }
 
@@ -527,9 +537,9 @@
      */
     private final class LocalService extends VrManagerInternal {
         @Override
-        public void setVrMode(boolean enabled, ComponentName packageName, int userId,
+        public void setVrMode(boolean enabled, ComponentName packageName, int userId, int processId,
                 ComponentName callingPackage) {
-            VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage);
+            VrManagerService.this.setVrMode(enabled, packageName, userId, processId, callingPackage);
         }
 
         @Override
@@ -704,14 +714,16 @@
      * Note: Must be called while holding {@code mLock}.
      *
      * @param enabled new state for VR mode.
+     * @param running2dInVr true if we have a top-level 2D intent.
      * @param component new component to be bound as a VR listener.
      * @param userId user owning the component to be bound.
-     * @param calling the component currently using VR mode.
+     * @param processId the process hosting the activity specified by calling.
+     * @param calling the component currently using VR mode or a 2D intent.
      *
      * @return {@code true} if the component/user combination specified is valid.
      */
-    private boolean updateCurrentVrServiceLocked(boolean enabled, @NonNull ComponentName component,
-            int userId, ComponentName calling) {
+    private boolean updateCurrentVrServiceLocked(boolean enabled, boolean running2dInVr,
+            @NonNull ComponentName component, int userId, int processId, ComponentName calling) {
 
         boolean sendUpdatedCaller = false;
         final long identity = Binder.clearCallingIdentity();
@@ -771,6 +783,8 @@
                 sendUpdatedCaller = true;
             }
             mCurrentVrModeComponent = calling;
+            mRunning2dInVr = running2dInVr;
+            mVrAppProcessId = processId;
 
             if (mCurrentVrModeUser != userId) {
                 mCurrentVrModeUser = userId;
@@ -788,11 +802,13 @@
 
             if (mCurrentVrService != null && sendUpdatedCaller) {
                 final ComponentName c = mCurrentVrModeComponent;
+                final boolean b = running2dInVr;
+                final int pid = processId;
                 mCurrentVrService.sendEvent(new PendingEvent() {
                     @Override
                     public void runEvent(IInterface service) throws RemoteException {
                         IVrListener l = (IVrListener) service;
-                        l.focusedActivityChanged(c);
+                        l.focusedActivityChanged(c, b, pid);
                     }
                 });
             }
@@ -1017,20 +1033,20 @@
      */
     private void consumeAndApplyPendingStateLocked(boolean disconnectIfNoPendingState) {
         if (mPendingState != null) {
-            updateCurrentVrServiceLocked(mPendingState.enabled,
-                    mPendingState.targetPackageName, mPendingState.userId,
+            updateCurrentVrServiceLocked(mPendingState.enabled, mPendingState.running2dInVr,
+                    mPendingState.targetPackageName, mPendingState.userId, mPendingState.processId,
                     mPendingState.callingPackage);
             mPendingState = null;
         } else if (disconnectIfNoPendingState) {
-            updateCurrentVrServiceLocked(false, null, 0, null);
+            updateCurrentVrServiceLocked(false, false, null, 0, -1, null);
         }
     }
 
     private void logStateLocked() {
         ComponentName currentBoundService = (mCurrentVrService == null) ? null :
             mCurrentVrService.getComponent();
-        VrState current = new VrState(mVrModeEnabled, currentBoundService, mCurrentVrModeUser,
-            mCurrentVrModeComponent, mWasDefaultGranted);
+        VrState current = new VrState(mVrModeEnabled, mRunning2dInVr, currentBoundService,
+            mCurrentVrModeUser, mVrAppProcessId, mCurrentVrModeComponent, mWasDefaultGranted);
         if (mLoggingDeque.size() == EVENT_LOG_SIZE) {
             mLoggingDeque.removeFirst();
         }
@@ -1074,27 +1090,24 @@
      * Implementation of VrManagerInternal calls.  These are callable from system services.
      */
     private void setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
-            int userId, @NonNull ComponentName callingPackage) {
+            int userId, int processId, @NonNull ComponentName callingPackage) {
 
         synchronized (mLock) {
             VrState pending;
             ComponentName targetListener;
-            ComponentName foregroundVrComponent;
 
             // If the device is in persistent VR mode, then calls to disable VR mode are ignored,
             // and the system default VR listener is used.
             boolean targetEnabledState = enabled || mPersistentVrModeEnabled;
-            if (!enabled && mPersistentVrModeEnabled) {
+            boolean running2dInVr = !enabled && mPersistentVrModeEnabled;
+            if (running2dInVr) {
                 targetListener = mDefaultVrService;
-
-                // Current foreground component isn't a VR one (in 2D app case)
-                foregroundVrComponent = null;
             } else {
                 targetListener = targetPackageName;
-                foregroundVrComponent = callingPackage;
             }
-            pending = new VrState(
-                    targetEnabledState, targetListener, userId, foregroundVrComponent);
+
+            pending = new VrState(targetEnabledState, running2dInVr, targetListener,
+                    userId, processId, callingPackage);
 
             if (!mVrModeAllowed) {
                 // We're not allowed to be in VR mode.  Make this state pending.  This will be
@@ -1119,8 +1132,8 @@
                 mPendingState = null;
             }
 
-            updateCurrentVrServiceLocked(
-                    targetEnabledState, targetListener, userId, foregroundVrComponent);
+            updateCurrentVrServiceLocked(targetEnabledState, running2dInVr, targetListener,
+                    userId, processId, callingPackage);
         }
     }
 
@@ -1129,7 +1142,7 @@
             setPersistentModeAndNotifyListenersLocked(enabled);
             // Disabling persistent mode when not showing a VR should disable the overall vr mode.
             if (!enabled && mCurrentVrModeComponent == null) {
-                setVrMode(false, null, 0, null);
+                setVrMode(false, null, 0, -1, null);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 05f4626..a37b2e5 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2992,14 +2992,14 @@
                 // Don't include wallpaper in bounds calculation
                 if (!w.mIsWallpaper && !mutableIncludeFullDisplay.value) {
                     if (includeDecor) {
-                        final TaskStack stack = w.getStack();
-                        if (stack != null) {
-                            stack.getBounds(frame);
-                        }
+                        final Task task = w.getTask();
+                        if (task != null) {
+                            task.getBounds(frame);
+                        } else {
 
-                        // We want to screenshot with the exact bounds of the surface of the app. Thus,
-                        // intersect it with the frame.
-                        frame.intersect(w.mFrame);
+                            // No task bounds? Too bad! Ain't no screenshot then.
+                            return true;
+                        }
                     } else {
                         final Rect wf = w.mFrame;
                         final Rect cr = w.mContentInsets;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index cc3b146..e5055e9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -624,6 +624,17 @@
         return token != null ? token.findMainWindow() : null;
     }
 
+    AppWindowToken getTopFullscreenAppToken() {
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final AppWindowToken token = mChildren.get(i);
+            final WindowState win = token.findMainWindow();
+            if (win != null && win.mAttrs.isFullscreen()) {
+                return token;
+            }
+        }
+        return null;
+    }
+
     AppWindowToken getTopVisibleAppToken() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             final AppWindowToken token = mChildren.get(i);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 469dab4..d7f0496 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -151,14 +151,27 @@
         final int currentOrientation;
         synchronized (service.mWindowMap) {
             final WindowState mainWindow = token.findMainWindow();
-            if (mainWindow == null) {
+            final Task task = token.getTask();
+            if (task == null) {
+                Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for token="
+                        + token);
+                return null;
+            }
+            final AppWindowToken topFullscreenToken = token.getTask().getTopFullscreenAppToken();
+            if (topFullscreenToken == null) {
+                Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find top fullscreen for task="
+                        + task);
+                return null;
+            }
+            final WindowState topFullscreenWindow = topFullscreenToken.findMainWindow();
+            if (mainWindow == null || topFullscreenWindow == null) {
                 Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for token="
                         + token);
                 return null;
             }
-            sysUiVis = mainWindow.getSystemUiVisibility();
-            windowFlags = mainWindow.getAttrs().flags;
-            windowPrivateFlags = mainWindow.getAttrs().privateFlags;
+            sysUiVis = topFullscreenWindow.getSystemUiVisibility();
+            windowFlags = topFullscreenWindow.getAttrs().flags;
+            windowPrivateFlags = topFullscreenWindow.getAttrs().privateFlags;
 
             layoutParams.dimAmount = mainWindow.getAttrs().dimAmount;
             layoutParams.type = TYPE_APPLICATION_STARTING;
@@ -172,22 +185,17 @@
             layoutParams.width = LayoutParams.MATCH_PARENT;
             layoutParams.height = LayoutParams.MATCH_PARENT;
             layoutParams.systemUiVisibility = sysUiVis;
-            final Task task = token.getTask();
-            if (task != null) {
-                layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));
+            layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));
 
-                final TaskDescription taskDescription = task.getTaskDescription();
-                if (taskDescription != null) {
-                    backgroundColor = taskDescription.getBackgroundColor();
-                    statusBarColor = taskDescription.getStatusBarColor();
-                    navigationBarColor = taskDescription.getNavigationBarColor();
-                }
-                taskBounds = new Rect();
-                task.getBounds(taskBounds);
-            } else {
-                taskBounds = null;
+            final TaskDescription taskDescription = task.getTaskDescription();
+            if (taskDescription != null) {
+                backgroundColor = taskDescription.getBackgroundColor();
+                statusBarColor = taskDescription.getStatusBarColor();
+                navigationBarColor = taskDescription.getNavigationBarColor();
             }
-            currentOrientation = mainWindow.getConfiguration().orientation;
+            taskBounds = new Rect();
+            task.getBounds(taskBounds);
+            currentOrientation = topFullscreenWindow.getConfiguration().orientation;
         }
         try {
             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
index 984a484..ea207f1 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
@@ -58,7 +58,7 @@
     }
 
     public void testTaskIdsPersistence() {
-        SparseBooleanArray taskIdsOnFile = mTaskPersister.loadPersistedTaskIdsForUser(testUserId);
+        SparseBooleanArray taskIdsOnFile = new SparseBooleanArray();
         for (int i = 0; i < 100; i++) {
             taskIdsOnFile.put(getRandomTaskIdForUser(testUserId), true);
         }