Merge "dismiss the volume shade if the a11y stream goes away" into oc-mr1-dev
diff --git a/Android.mk b/Android.mk
index 1e6041f..19bbc5f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -172,6 +172,7 @@
 	core/java/android/content/pm/IPackageInstallerCallback.aidl \
 	core/java/android/content/pm/IPackageInstallerSession.aidl \
 	core/java/android/content/pm/IPackageManager.aidl \
+	../native/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl \
 	core/java/android/content/pm/IPackageMoveObserver.aidl \
 	core/java/android/content/pm/IPackageStatsObserver.aidl \
 	core/java/android/content/pm/IPinItemRequest.aidl \
diff --git a/api/current.txt b/api/current.txt
index 3c9cd8e..434e9da1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10172,6 +10172,7 @@
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
     method public static java.lang.CharSequence getCategoryTitle(android.content.Context, int);
+    method public boolean isVirtualPreload();
     method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager);
     field public static final int CATEGORY_AUDIO = 1; // 0x1
     field public static final int CATEGORY_GAME = 0; // 0x0
@@ -11909,7 +11910,7 @@
     method public boolean needUpgrade(int);
     method protected void onAllReferencesReleased();
     method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int);
-    method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.OpenParams);
+    method public static android.database.sqlite.SQLiteDatabase openDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.OpenParams);
     method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.CursorFactory);
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory);
diff --git a/api/system-current.txt b/api/system-current.txt
index 6c1983d..604354bf 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -10760,6 +10760,7 @@
     method public int describeContents();
     method public void dump(android.util.Printer, java.lang.String);
     method public static java.lang.CharSequence getCategoryTitle(android.content.Context, int);
+    method public boolean isVirtualPreload();
     method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager);
     field public static final int CATEGORY_AUDIO = 1; // 0x1
     field public static final int CATEGORY_GAME = 0; // 0x0
@@ -12713,7 +12714,7 @@
     method public boolean needUpgrade(int);
     method protected void onAllReferencesReleased();
     method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int);
-    method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.OpenParams);
+    method public static android.database.sqlite.SQLiteDatabase openDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.OpenParams);
     method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.CursorFactory);
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory);
diff --git a/api/test-current.txt b/api/test-current.txt
index 839964f..be1e314 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10209,6 +10209,7 @@
     method public static java.lang.CharSequence getCategoryTitle(android.content.Context, int);
     method public boolean isPrivilegedApp();
     method public boolean isSystemApp();
+    method public boolean isVirtualPreload();
     method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager);
     field public static final int CATEGORY_AUDIO = 1; // 0x1
     field public static final int CATEGORY_GAME = 0; // 0x0
@@ -11953,7 +11954,7 @@
     method public boolean needUpgrade(int);
     method protected void onAllReferencesReleased();
     method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int);
-    method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.OpenParams);
+    method public static android.database.sqlite.SQLiteDatabase openDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.OpenParams);
     method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.CursorFactory);
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory);
diff --git a/cmds/svc/src/com/android/commands/svc/WifiCommand.java b/cmds/svc/src/com/android/commands/svc/WifiCommand.java
index 633dd97..e31cb53 100644
--- a/cmds/svc/src/com/android/commands/svc/WifiCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/WifiCommand.java
@@ -51,6 +51,10 @@
             if (validCommand) {
                 IWifiManager wifiMgr
                         = IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE));
+                if (wifiMgr == null) {
+                    System.err.println("Wi-Fi service is not ready");
+                    return;
+                }
                 try {
                     wifiMgr.setWifiEnabled("com.android.shell", flag);
                 }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0bfe567..72075a5 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -572,7 +572,7 @@
     public static final int PRIVATE_FLAG_STATIC_SHARED_LIBRARY = 1 << 14;
 
     /**
-     * Value for {@linl #privateFlags}: When set, the application will only have its splits loaded
+     * Value for {@link #privateFlags}: When set, the application will only have its splits loaded
      * if they are required to load a component. Splits can be loaded on demand using the
      * {@link Context#createContextForSplit(String)} API.
      * @hide
@@ -580,10 +580,40 @@
     public static final int PRIVATE_FLAG_ISOLATED_SPLIT_LOADING = 1 << 15;
 
     /**
-     * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
-     * {@hide}
+     * Value for {@link #privateFlags}: When set, the application was installed as
+     * a virtual preload.
+     * @hide
      */
-    public int privateFlags;
+    public static final int PRIVATE_FLAG_VIRTUAL_PRELOAD = 1 << 16;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
+            PRIVATE_FLAG_HIDDEN,
+            PRIVATE_FLAG_CANT_SAVE_STATE,
+            PRIVATE_FLAG_FORWARD_LOCK,
+            PRIVATE_FLAG_PRIVILEGED,
+            PRIVATE_FLAG_HAS_DOMAIN_URLS,
+            PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
+            PRIVATE_FLAG_DIRECT_BOOT_AWARE,
+            PRIVATE_FLAG_INSTANT,
+            PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
+            PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
+            PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
+            PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE,
+            PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION,
+            PRIVATE_FLAG_BACKUP_IN_FOREGROUND,
+            PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
+            PRIVATE_FLAG_ISOLATED_SPLIT_LOADING,
+            PRIVATE_FLAG_VIRTUAL_PRELOAD,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApplicationInfoPrivateFlags {}
+
+    /**
+     * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
+     * @hide
+     */
+    public @ApplicationInfoPrivateFlags int privateFlags;
 
     /**
      * @hide
@@ -1509,6 +1539,13 @@
     }
 
     /**
+     * Returns whether or not this application was installed as a virtual preload.
+     */
+    public boolean isVirtualPreload() {
+        return (privateFlags & PRIVATE_FLAG_VIRTUAL_PRELOAD) != 0;
+    }
+
+    /**
      * @hide
      */
     @Override protected ApplicationInfo getApplicationInfo() {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 1921b2a..0e70645 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -61,12 +61,6 @@
  *  {@hide}
  */
 interface IPackageManager {
-    // Since these transactions are also called from native code, these must be kept in sync with
-    // the ones in frameworks/native/include/binder/IPackageManager.h
-    // =============== Beginning of transactions used on native side as well ======================
-    String[] getNamesForUids(in int[] uids);
-    // =============== End of transactions used on native side as well ============================
-
     void checkPackageStartable(String packageName, int userId);
     boolean isPackageAvailable(String packageName, int userId);
     PackageInfo getPackageInfo(String packageName, int flags, int userId);
@@ -134,6 +128,7 @@
     String[] getPackagesForUid(int uid);
 
     String getNameForUid(int uid);
+    String[] getNamesForUids(in int[] uids);
 
     int getUidForSharedUser(String sharedUserName);
 
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4ae1aaf..b6ed95a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3002,7 +3002,7 @@
         if (procSeq == null || procSeq.length() <= 0) {
             return defProc;
         }
-        return buildCompoundName(pkg, procSeq, "process", outError);
+        return TextUtils.safeIntern(buildCompoundName(pkg, procSeq, "process", outError));
     }
 
     private static String buildTaskAffinityName(String pkg, String defProc,
@@ -6930,6 +6930,11 @@
         } else {
             ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_INSTANT;
         }
+        if (state.virtualPreload) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD;
+        } else {
+            ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD;
+        }
         if (state.hidden) {
             ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
         } else {
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 470336c..069b2d4 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -21,10 +21,10 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
-import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 
 import android.util.ArraySet;
@@ -45,6 +45,7 @@
     public boolean hidden; // Is the app restricted by owner / admin
     public boolean suspended;
     public boolean instantApp;
+    public boolean virtualPreload;
     public int enabled;
     public String lastDisableAppCaller;
     public int domainVerificationStatus;
@@ -75,6 +76,7 @@
         hidden = o.hidden;
         suspended = o.suspended;
         instantApp = o.instantApp;
+        virtualPreload = o.virtualPreload;
         enabled = o.enabled;
         lastDisableAppCaller = o.lastDisableAppCaller;
         domainVerificationStatus = o.domainVerificationStatus;
@@ -194,6 +196,9 @@
         if (instantApp != oldState.instantApp) {
             return false;
         }
+        if (virtualPreload != oldState.virtualPreload) {
+            return false;
+        }
         if (enabled != oldState.enabled) {
             return false;
         }
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 5b6efd4..de02ee5 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -703,12 +703,19 @@
     /**
      * Open the database according to the specified {@link OpenParams parameters}
      *
-     * @param path to database file to open and/or create
+     * @param path path to database file to open and/or create.
+     * <p><strong>Important:</strong> The file should be constructed either from an absolute path or
+     * by using {@link android.content.Context#getDatabasePath(String)}.
      * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}
      * @return the newly opened database
      * @throws SQLiteException if the database cannot be opened
      */
-    public static SQLiteDatabase openDatabase(@NonNull String path,
+    public static SQLiteDatabase openDatabase(@NonNull File path,
+            @NonNull OpenParams openParams) {
+        return openDatabase(path.getPath(), openParams);
+    }
+
+    private static SQLiteDatabase openDatabase(@NonNull String path,
             @NonNull OpenParams openParams) {
         Preconditions.checkArgument(openParams != null, "OpenParams cannot be null");
         SQLiteDatabase db = new SQLiteDatabase(path, openParams.mOpenFlags,
@@ -873,7 +880,8 @@
      *
      * @param factory an optional factory class that is called to instantiate a
      *            cursor when query is called
-     * @return a SQLiteDatabase object, or null if the database can't be created
+     * @return a SQLiteDatabase instance
+     * @throws SQLiteException if the database cannot be created
      */
     @NonNull
     public static SQLiteDatabase create(@Nullable CursorFactory factory) {
@@ -889,7 +897,8 @@
      * <p>Sets the locale of the database to the  the system's current locale.
      * Call {@link #setLocale} if you would like something else.</p>
      * @param openParams configuration parameters that are used for opening SQLiteDatabase
-     * @return a SQLiteDatabase object, or null if the database can't be created
+     * @return a SQLiteDatabase instance
+     * @throws SQLException if the database cannot be created
      */
     @NonNull
     public static SQLiteDatabase createInMemory(@NonNull OpenParams openParams) {
@@ -2322,7 +2331,7 @@
         }
 
         /**
-         * Returns flags to control database access mode
+         * Returns flags to control database access mode. Default value is 0.
          *
          * @see Builder#setOpenFlags(int)
          */
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index dfaf714..cc9e0f4 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -289,12 +289,12 @@
             } else if (mName == null) {
                 db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
             } else {
-                final String path = mContext.getDatabasePath(mName).getPath();
+                final File filePath = mContext.getDatabasePath(mName);
                 SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
                 try {
-                    db = SQLiteDatabase.openDatabase(path, params);
+                    db = SQLiteDatabase.openDatabase(filePath, params);
                     // Keep pre-O-MR1 behavior by resetting file permissions to 660
-                    setFilePermissionsForDb(path);
+                    setFilePermissionsForDb(filePath.getPath());
                 } catch (SQLException ex) {
                     if (writable) {
                         throw ex;
@@ -302,7 +302,7 @@
                     Log.e(TAG, "Couldn't open " + mName
                             + " for writing (will try read-only):", ex);
                     params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
-                    db = SQLiteDatabase.openDatabase(path, params);
+                    db = SQLiteDatabase.openDatabase(filePath, params);
                 }
             }
 
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 233c4d4..8b6f9c1 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -106,6 +106,7 @@
 
    /**
      * The operation was canceled because the API is locked out due to too many attempts.
+     * This occurs after 5 failed attempts, and lasts for 30 seconds.
      */
     public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
 
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 77ce65b..be9e809 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -672,36 +672,33 @@
             entry.tag = left.tag[i];
             entry.metered = left.metered[i];
             entry.roaming = left.roaming[i];
+            entry.rxBytes = left.rxBytes[i];
+            entry.rxPackets = left.rxPackets[i];
+            entry.txBytes = left.txBytes[i];
+            entry.txPackets = left.txPackets[i];
+            entry.operations = left.operations[i];
 
             // find remote row that matches, and subtract
             final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag,
                     entry.metered, entry.roaming, i);
-            if (j == -1) {
-                // newly appearing row, return entire value
-                entry.rxBytes = left.rxBytes[i];
-                entry.rxPackets = left.rxPackets[i];
-                entry.txBytes = left.txBytes[i];
-                entry.txPackets = left.txPackets[i];
-                entry.operations = left.operations[i];
-            } else {
-                // existing row, subtract remote value
-                entry.rxBytes = left.rxBytes[i] - right.rxBytes[j];
-                entry.rxPackets = left.rxPackets[i] - right.rxPackets[j];
-                entry.txBytes = left.txBytes[i] - right.txBytes[j];
-                entry.txPackets = left.txPackets[i] - right.txPackets[j];
-                entry.operations = left.operations[i] - right.operations[j];
+            if (j != -1) {
+                // Found matching row, subtract remote value.
+                entry.rxBytes -= right.rxBytes[j];
+                entry.rxPackets -= right.rxPackets[j];
+                entry.txBytes -= right.txBytes[j];
+                entry.txPackets -= right.txPackets[j];
+                entry.operations -= right.operations[j];
+            }
 
-                if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
-                        || entry.txPackets < 0 || entry.operations < 0) {
-                    if (observer != null) {
-                        observer.foundNonMonotonic(left, i, right, j, cookie);
-                    }
-                    entry.rxBytes = Math.max(entry.rxBytes, 0);
-                    entry.rxPackets = Math.max(entry.rxPackets, 0);
-                    entry.txBytes = Math.max(entry.txBytes, 0);
-                    entry.txPackets = Math.max(entry.txPackets, 0);
-                    entry.operations = Math.max(entry.operations, 0);
+            if (entry.isNegative()) {
+                if (observer != null) {
+                    observer.foundNonMonotonic(left, i, right, j, cookie);
                 }
+                entry.rxBytes = Math.max(entry.rxBytes, 0);
+                entry.rxPackets = Math.max(entry.rxPackets, 0);
+                entry.txBytes = Math.max(entry.txBytes, 0);
+                entry.txPackets = Math.max(entry.txPackets, 0);
+                entry.operations = Math.max(entry.operations, 0);
             }
 
             result.addValues(entry);
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 440c88e..3e64af4 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -484,6 +484,14 @@
     }
 
     /**
+     * @return interned string if it's null.
+     * @hide
+     */
+    public static String safeIntern(String s) {
+        return (s != null) ? s.intern() : null;
+    }
+
+    /**
      * Returns the length that the specified CharSequence would have if
      * spaces and ASCII control characters were trimmed from the start and end,
      * as by {@link String#trim}.
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index e2ad1e0..29e5523 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -52,9 +52,64 @@
 import java.util.Objects;
 
 /**
- * App entry point to the Autofill Framework.
+ * The {@link AutofillManager} provides ways for apps and custom views to integrate with the
+ * Autofill Framework lifecycle.
  *
- * <p>It is safe to call into this from any thread.
+ * <p>The autofill lifecycle starts with the creation of an autofill context associated with an
+ * activity context; the autofill context is created when one of the following methods is called for
+ * the first time in an activity context, and the current user has an enabled autofill service:
+ *
+ * <ul>
+ *   <li>{@link #notifyViewEntered(View)}
+ *   <li>{@link #notifyViewEntered(View, int, Rect)}
+ *   <li>{@link #requestAutofill(View)}
+ * </ul>
+ *
+ * <p>Tipically, the context is automatically created when the first view of the activity is
+ * focused because {@code View.onFocusChanged()} indirectly calls
+ * {@link #notifyViewEntered(View)}. App developers can call {@link #requestAutofill(View)} to
+ * explicitly create it (for example, a custom view developer could offer a contextual menu action
+ * in a text-field view to let users manually request autofill).
+ *
+ * <p>After the context is created, the Android System creates a {@link android.view.ViewStructure}
+ * that represents the view hierarchy by calling
+ * {@link View#dispatchProvideAutofillStructure(android.view.ViewStructure, int)} in the root views
+ * of all application windows. By default, {@code dispatchProvideAutofillStructure()} results in
+ * subsequent calls to {@link View#onProvideAutofillStructure(android.view.ViewStructure, int)} and
+ * {@link View#onProvideAutofillVirtualStructure(android.view.ViewStructure, int)} for each view in
+ * the hierarchy.
+ *
+ * <p>The resulting {@link android.view.ViewStructure} is then passed to the autofill service, which
+ * parses it looking for views that can be autofilled. If the service finds such views, it returns
+ * a data structure to the Android System containing the following optional info:
+ *
+ * <ul>
+ *   <li>Datasets used to autofill subsets of views in the activity.
+ *   <li>Id of views that the service can save their values for future autofilling.
+ * </ul>
+ *
+ * <p>When the service returns datasets, the Android System displays an autofill dataset picker
+ * UI affordance associated with the view, when the view is focused on and is part of a dataset.
+ * The application can be notified when the affordance is shown by registering an
+ * {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
+ * selects a dataset from the affordance, all views present in the dataset are autofilled, through
+ * calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
+ *
+ * <p>When the service returns ids of savable views, the Android System keeps track of changes
+ * made to these views, so they can be used to determine if the autofill save UI is shown later.
+ *
+ * <p>The context is then finished when one of the following occurs:
+ *
+ * <ul>
+ *   <li>{@link #commit()} is called or all savable views are gone.
+ *   <li>{@link #cancel()} is called.
+ * </ul>
+ *
+ * <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
+ * shows a save UI affordance if the value of savable views have changed. If the user selects the
+ * option to Save, the current value of the views is then sent to the autofill service.
+ *
+ * <p>It is safe to call into its methods from any thread.
  */
 @SystemService(Context.AUTOFILL_MANAGER_SERVICE)
 public final class AutofillManager {
@@ -1494,10 +1549,10 @@
     }
 
     /**
-     * Callback for auto-fill related events.
+     * Callback for autofill related events.
      *
      * <p>Typically used for applications that display their own "auto-complete" views, so they can
-     * enable / disable such views when the auto-fill UI affordance is shown / hidden.
+     * enable / disable such views when the autofill UI affordance is shown / hidden.
      */
     public abstract static class AutofillCallback {
 
@@ -1507,7 +1562,7 @@
         public @interface AutofillEventType {}
 
         /**
-         * The auto-fill input UI affordance associated with the view was shown.
+         * The autofill input UI affordance associated with the view was shown.
          *
          * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
          * should be hidden upon receiving this event.
@@ -1515,7 +1570,7 @@
         public static final int EVENT_INPUT_SHOWN = 1;
 
         /**
-         * The auto-fill input UI affordance associated with the view was hidden.
+         * The autofill input UI affordance associated with the view was hidden.
          *
          * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
          * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
@@ -1523,7 +1578,7 @@
         public static final int EVENT_INPUT_HIDDEN = 2;
 
         /**
-         * The auto-fill input UI affordance associated with the view won't be shown because
+         * The autofill input UI affordance associated with the view isn't shown because
          * autofill is not available.
          *
          * <p>If the view provides its own auto-complete UI affordance but was not displaying it
diff --git a/core/java/android/webkit/SafeBrowsingResponse.java b/core/java/android/webkit/SafeBrowsingResponse.java
index 024a70b..2ccd2ba 100644
--- a/core/java/android/webkit/SafeBrowsingResponse.java
+++ b/core/java/android/webkit/SafeBrowsingResponse.java
@@ -18,9 +18,9 @@
 
 /**
  * Used to indicate an action to take when hitting a malicious URL. Instances of this class are
- * created by the WebView and passed to {@link WebViewClient#onSafeBrowsingHit}. The host
- * application must call {@link #showInterstitial}, {@link #proceed}, or {@link #backToSafety} to
- * set the WebView's response to the Safe Browsing hit.
+ * created by the WebView and passed to {@link android.webkit.WebViewClient#onSafeBrowsingHit}. The
+ * host application must call {@link #showInterstitial(boolean)}, {@link #proceed(boolean)}, or
+ * {@link #backToSafety(boolean)} to set the WebView's response to the Safe Browsing hit.
  */
 public abstract class SafeBrowsingResponse {
 
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 3a6de96..82cff7c 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1390,17 +1390,15 @@
      * Sets whether Safe Browsing is enabled. Safe browsing allows WebView to
      * protect against malware and phishing attacks by verifying the links.
      *
-     * Safe browsing is disabled by default. The recommended way to enable
-     * Safe browsing is using a manifest tag to change the default value to
-     * enabled for all WebViews.
      * <p>
-     * <pre>
-     * &lt;meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
-     *            android:value="true" /&gt;
-     * </pre>
+     * Safe browsing is disabled by default. The recommended way to enable Safe browsing is using a
+     * manifest tag to change the default value to enabled for all WebViews (read <a
+     * href="{@docRoot}reference/android/webkit/WebView.html">general Safe Browsing info</a>).
      * </p>
      *
+     * <p>
      * This API overrides the manifest tag value for this WebView.
+     * </p>
      *
      * @param enabled Whether Safe browsing is enabled.
      */
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index da77640..dd716eb 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -319,6 +319,22 @@
  * out.
  * </p>
  *
+ * <h3>Safe Browsing</h3>
+ *
+ * <p>
+ * If Safe Browsing is enabled, WebView will block malicious URLs and present a warning UI to the
+ * user to allow them to navigate back safely or proceed to the malicious page.
+ * </p>
+ * <p>
+ * The recommended way for apps to enable the feature is putting the following tag in the manifest:
+ * </p>
+ * <p>
+ * <pre>
+ * &lt;meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
+ *            android:value="true" /&gt;
+ * </pre>
+ * </p>
+ *
  */
 // Implementation notes.
 // The WebView is a thin API class that delegates its public API to a backend WebViewProvider
@@ -1622,8 +1638,24 @@
     }
 
     /**
-     * Starts Safe Browsing initialization. This should only be called once. This does not require
-     * an Activity Context, and will always use the application Context to do its work.
+     * Starts Safe Browsing initialization. This should only be called once.
+     *
+     * <p>
+     * Because the Safe Browsing feature takes time to initialize, WebView may temporarily whitelist
+     * URLs until the feature is ready. The callback will be invoked with true once initialization
+     * is complete.
+     * </p>
+     *
+     * <p>
+     * This does not enable the Safe Browsing feature itself, and should only be used if the feature
+     * is otherwise enabled.
+     * </p>
+     *
+     * <p>
+     * This does not require an Activity Context, and will always use the application Context to do
+     * its work.
+     * </p>
+     *
      * @param context Application Context.
      * @param callback will be called with the value true if initialization is
      * successful. The callback will be run on the UI thread.
@@ -1633,7 +1665,9 @@
     }
 
     /**
-     * Shuts down Safe Browsing. This should only be called once.
+     * Shuts down Safe Browsing. This should only be called once. This does not disable the feature,
+     * it only frees resources used by Safe Browsing code. To disable Safe Browsing on an individual
+     * WebView, see {@link WebSettings#setSafeBrowsingEnabled}
      */
     public static void shutdownSafeBrowsing() {
         getFactory().getStatics().shutdownSafeBrowsing();
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index 1168eec..1811800c 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -111,7 +111,7 @@
         return mContext.getResources().getString(R.string.config_dozeComponent);
     }
 
-    private boolean accessibilityInversionEnabled(int user) {
+    public boolean accessibilityInversionEnabled(int user) {
         return boolSettingDefaultOff(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, user);
     }
 
diff --git a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
index 7295380..c89f546 100644
--- a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
@@ -134,9 +134,9 @@
                         .append(uid).append("\n");
                 sb.append("data=").append("(").append(uidTimeMs[i]).append(",")
                         .append(totalTimeMs).append(")").append("\n");
-                sb.append("times=").append("(")
-                        .append(TimeUtils.formatForLogging(mLastTimeReadMs)).append(",")
-                        .append(TimeUtils.formatForLogging(mNowTimeMs)).append(")");
+                sb.append("times=").append("(");
+                TimeUtils.formatDuration(mLastTimeReadMs, sb); sb.append(",");
+                TimeUtils.formatDuration(mNowTimeMs, sb); sb.append(")");
                 Slog.wtf(TAG, sb.toString());
                 return;
             }
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 45a8654..f978b57 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -116,4 +116,6 @@
     void remQsTile(in ComponentName tile);
     void clickQsTile(in ComponentName tile);
     void handleSystemKey(in int key);
+
+    void showShutdownUi(boolean isReboot, String reason);
 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index e6c49f1..0d15758 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1211,6 +1211,11 @@
      */
     public long setLockoutAttemptDeadline(int userId, int timeoutMs) {
         final long deadline = SystemClock.elapsedRealtime() + timeoutMs;
+        if (userId == USER_FRP) {
+            // For secure password storage (that is required for FRP), the underlying storage also
+            // enforces the deadline. Since we cannot store settings for the FRP user, don't.
+            return deadline;
+        }
         setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, userId);
         setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, timeoutMs, userId);
         return deadline;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0289a8a..208c776 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -305,7 +305,7 @@
     <protected-broadcast android:name="com.android.server.WifiManager.action.DEVICE_IDLE" />
     <protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_ACCEPTED" />
     <protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_DECLINED" />
-    <protected-broadcast android:name="com.android.server.action.WIPE_EUICC_DATA" />
+    <protected-broadcast android:name="com.android.internal.action.EUICC_FACTORY_RESET" />
     <protected-broadcast android:name="com.android.server.usb.ACTION_OPEN_IN_APPS" />
     <protected-broadcast android:name="com.android.server.am.DELETE_DUMPHEAP" />
     <protected-broadcast android:name="com.android.server.net.action.SNOOZE_WARNING" />
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index d62b93e..cf67abf 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -39,4 +39,7 @@
 
     <!-- Whether the device uses the default focus highlight when focus state isn't specified. -->
     <bool name="config_useDefaultFocusHighlight">false</bool>
+
+    <!-- Allow SystemUI to show the shutdown dialog -->
+    <bool name="config_showSysuiShutdown">false</bool>
 </resources>
diff --git a/core/res/res/values-watch/colors_device_defaults.xml b/core/res/res/values-watch/colors_device_defaults.xml
index 15786b4..654e92c 100644
--- a/core/res/res/values-watch/colors_device_defaults.xml
+++ b/core/res/res/values-watch/colors_device_defaults.xml
@@ -17,8 +17,87 @@
 <!-- Colors specific to DeviceDefault themes. These are mostly pass-throughs to enable
      overlaying new theme colors. -->
 <resources>
+
+    <!--
+       primary_device_default_dark
+         > from values/colors_material/primary_material_dark
+         > from values/colors_material/material_grey_900
+         = #ff212121
+         ! replaced with custom color #33ffffff
+    -->
+    <color name="primary_device_default_dark">#33ffffff</color>
+    <!--
+       primary_dark_device_default_dark
+         > from values/colors_material/primary_dark_material_dark
+         = @color/black
+    -->
+    <color name="primary_dark_device_default_dark">@color/black</color>
+    <!--
+       accent_device_default_dark
+         > from values/colors_material/accent_material_dark
+         > from values/colors_material/material_deep_teal_200
+         = #ff80cbc4
+         ! replaced with custom color #5E97F6
+    -->
+    <color name="accent_device_default_dark">#5E97F6</color>
+    <!--
+       background_device_default_dark
+         > from values/colors_material/background_material_dark
+         > from values/colors_material/material_grey_850
+         = #ff303030
+         ! replaced with custom color #232E33
+    -->
+    <color name="background_device_default_dark">#232E33</color>
+    <!--
+       background_floating_device_default_dark
+         > from values/colors_material/background_floating_material_dark
+         > from values/colors_material/material_grey_800
+         = #ff424242
+         ! replaced with custom color #3E5059
+    -->
+    <color name="background_floating_device_default_dark">#3E5059</color>
+
+    <!--
+       background_cache_hint_selector_device_default
+         - note that this is based off of colors/background_cache_hint_selector_device_default
+           xml drawable
+         - uses ?attr/colorBackground and transparency to draw
+         - no color customization required here
+    -->
+
+    <!--
+       button_normal_device_default_dark
+         - uses ?attr/disabledAlpha and button_material_dark to draw
+         - cloned to watch_btn_default.xml drawable
+           (btn_default_material_dark & button_material_dark - see
+           values-watch/colors_material.xml)
+    -->
     <color name="button_normal_device_default_dark">@color/btn_default_material_dark</color>
+
     <!-- Use the same value as for accent_device_default_dark but start with #99,
          i.e. 60% opacity -->
     <color name="accent_device_default_dark_60_percent_opacity">#995E97f6</color>
+
+    <!--
+       foreground_device_default_dark
+         - introduced to avoid coupling to foreground_material_dark
+         - colorForeground typically falls through Theme.DeviceDefault to Theme.Material
+    -->
+    <color name="foreground_device_default_dark">@color/white</color>
+
+    <!--
+       error_color_device_default_dark
+         - introduced to avoid coupling to error_color_material (also #F4511E)
+         - colorError typically falls through Theme.DeviceDefault to Theme.Material
+    -->
+    <color name="error_color_device_default_dark">#F4511E</color>
+
+    <!-- deprecated for Wear
+         these overrides exist only for compatibility with existing
+         WTS theme test heuristics, based on the previous modifications
+         to the material theme, they should not be used for customization
+         as they are not exposed via publicly accessible attributes -->
+    <color name="accent_device_default_700">#5385DB</color>
+    <color name="accent_device_default_light">#75A4F5</color>
+    <color name="accent_device_default_50">#93B7F5</color>
 </resources>
diff --git a/core/res/res/values-watch/colors_material.xml b/core/res/res/values-watch/colors_material.xml
index 72f589b..b19820c 100644
--- a/core/res/res/values-watch/colors_material.xml
+++ b/core/res/res/values-watch/colors_material.xml
@@ -14,15 +14,8 @@
      limitations under the License.
 -->
 <resources>
-    <color name="background_material_dark">#232E33</color>
-    <color name="background_floating_material_dark">#3E5059</color>
 
-    <color name="accent_material_700">#5385DB</color>
-    <color name="accent_material_light">#75A4F5</color>
-    <color name="accent_material_dark">#5E97F6</color>
-    <color name="accent_material_50">#93B7F5</color>
+  <!-- referenced in colors/watch_btn_default.xml selector -->
+  <color name="button_material_dark">#ff919699</color>
 
-    <color name="primary_material_dark">#33ffffff</color>
-
-    <color name="button_material_dark">#ff919699</color>
 </resources>
diff --git a/core/res/res/values-watch/dimens_device_defaults.xml b/core/res/res/values-watch/dimens_device_defaults.xml
new file mode 100644
index 0000000..ac0fcb8
--- /dev/null
+++ b/core/res/res/values-watch/dimens_device_defaults.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+  <!-- Alpha transparency for wigets that set enablement/disablement programmatically
+       transparency is applied in the disabled state -->
+    <dimen name="disabled_alpha_device_default">0.5</dimen>
+  <!-- Alpha transparency applied to elements which are considered primary (e.g. primary text) -->
+    <dimen name="primary_content_alpha_device_default">1.0</dimen>
+  <!-- Alpha transparency applied to elements which are considered secondary (e.g. secondary text) -->
+    <dimen name="secondary_content_alpha_device_default">0.8</dimen>
+</resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 4d210f6..f6752c2 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -37,11 +37,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar -->
@@ -49,11 +54,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar.  This theme
@@ -62,11 +72,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -77,11 +92,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -91,11 +111,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Theme used for the intent picker activity. -->
@@ -134,11 +159,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for a window that should look like the Settings app.  -->
@@ -156,11 +186,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.CompactMenu" parent="Theme.Material.CompactMenu">
@@ -168,11 +203,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -182,11 +222,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -195,11 +240,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -209,11 +259,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
    <!-- DeviceDefault theme for a window that will be displayed either full-screen on smaller
@@ -222,11 +277,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -236,11 +296,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -248,11 +313,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -262,11 +332,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -275,11 +350,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -288,11 +368,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -301,11 +386,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -314,22 +404,32 @@
         <!-- Color palette Dialog -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar">
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">@color/background_device_default_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -337,10 +437,15 @@
         <!-- Color palette Dialog -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">?attr/colorBackgroundFloating</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
         <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4b64e3f..961d639 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3039,4 +3039,7 @@
 
     <!-- Enable the RingtonePickerActivity in 'com.android.providers.media'. -->
     <bool name="config_defaultRingtonePickerEnabled">true</bool>
+
+    <!-- Allow SystemUI to show the shutdown dialog -->
+    <bool name="config_showSysuiShutdown">true</bool>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e5b450c..9dc6921 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3061,4 +3061,5 @@
   <java-symbol type="bool" name="config_showAreaUpdateInfoSettings" />
   <java-symbol type="layout" name="shutdown_dialog" />
   <java-symbol type="dimen" name="chooser_service_spacing" />
+  <java-symbol type="bool" name="config_showSysuiShutdown" />
 </resources>
diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
index f97d51d..335cea7 100644
--- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
@@ -1058,7 +1058,7 @@
         mDatabase.close();
         SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder()
                 .setLookasideConfig(0, 0).build();
-        mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile.getPath(), params);
+        mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile, params);
         verifyLookasideStats(true);
     }
 
@@ -1193,7 +1193,7 @@
         mDatabase.close();
         SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder()
                 .setIdleConnectionTimeout(1000).build();
-        mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile.getPath(), params);
+        mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile, params);
         // Wait a bit and check that connection is still open
         Thread.sleep(100);
         String output = executeShellCommand("dumpsys dbinfo " + getContext().getPackageName());
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index ebaf10a..fd5b70e 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -879,7 +879,7 @@
     <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
     <string name="battery_info_status_discharging">Not charging</string>
     <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
-    <string name="battery_info_status_not_charging">Not charging</string>
+    <string name="battery_info_status_not_charging">Plugged in, can\'t charge right now</string>
     <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
     <string name="battery_info_status_full">Full</string>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
index f9dc0e4..00f32b2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
+++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
@@ -91,28 +91,25 @@
 
     // Shared prefs keys for storing dismissed state.
     // Index into current dismissed state.
-    @VisibleForTesting
-    static final String DISMISS_INDEX = "_dismiss_index";
     public static final String SETUP_TIME = "_setup_time";
     private static final String IS_DISMISSED = "_is_dismissed";
 
     // Default dismiss control for smart suggestions.
-    private static final String DEFAULT_SMART_DISMISS_CONTROL = "0,10";
+    private static final String DEFAULT_SMART_DISMISS_CONTROL = "0";
 
     private final Context mContext;
     private final List<SuggestionCategory> mSuggestionList;
     private final ArrayMap<Pair<String, String>, Tile> mAddCache = new ArrayMap<>();
     private final SharedPreferences mSharedPrefs;
-    private final String mSmartDismissControl;
-
+    private final String mDefaultDismissControl;
 
     public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml,
-            String smartDismissControl) {
+            String defaultDismissControl) {
         this(
                 context,
                 sharedPrefs,
                 (List<SuggestionCategory>) new SuggestionOrderInflater(context).parse(orderXml),
-                smartDismissControl);
+                defaultDismissControl);
     }
 
     public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) {
@@ -124,11 +121,11 @@
             Context context,
             SharedPreferences sharedPrefs,
             List<SuggestionCategory> suggestionList,
-            String smartDismissControl) {
+            String defaultDismissControl) {
         mContext = context;
         mSuggestionList = suggestionList;
         mSharedPrefs = sharedPrefs;
-        mSmartDismissControl = smartDismissControl;
+        mDefaultDismissControl = defaultDismissControl;
     }
 
     public SuggestionList getSuggestions(boolean isSmartSuggestionEnabled) {
@@ -161,25 +158,16 @@
         return suggestionList;
     }
 
-    public boolean dismissSuggestion(Tile suggestion) {
-        return dismissSuggestion(suggestion, false);
-    }
-
     /**
      * Dismisses a suggestion, returns true if the suggestion has no more dismisses left and should
      * be disabled.
      */
-    public boolean dismissSuggestion(Tile suggestion, boolean isSmartSuggestionEnabled) {
-        String keyBase = suggestion.intent.getComponent().flattenToShortString();
-        int index = mSharedPrefs.getInt(keyBase + DISMISS_INDEX, 0);
-        String dismissControl = getDismissControl(suggestion, isSmartSuggestionEnabled);
-        if (dismissControl == null || parseDismissString(dismissControl).length == index) {
-            return true;
-        }
+    public boolean dismissSuggestion(Tile suggestion) {
+        final String keyBase = suggestion.intent.getComponent().flattenToShortString();
         mSharedPrefs.edit()
                 .putBoolean(keyBase + IS_DISMISSED, true)
                 .commit();
-        return false;
+        return true;
     }
 
     @VisibleForTesting
@@ -357,32 +345,29 @@
     @VisibleForTesting
     boolean isDismissed(Tile suggestion, boolean isSmartSuggestionEnabled) {
         String dismissControl = getDismissControl(suggestion, isSmartSuggestionEnabled);
-        if (dismissControl == null) {
-            return false;
-        }
         String keyBase = suggestion.intent.getComponent().flattenToShortString();
         if (!mSharedPrefs.contains(keyBase + SETUP_TIME)) {
             mSharedPrefs.edit()
                     .putLong(keyBase + SETUP_TIME, System.currentTimeMillis())
                     .commit();
         }
-        // Default to dismissed, so that we can have suggestions that only first appear after
-        // some number of days.
-        if (!mSharedPrefs.getBoolean(keyBase + IS_DISMISSED, true)) {
-            return false;
-        }
-        int index = mSharedPrefs.getInt(keyBase + DISMISS_INDEX, 0);
-        int[] dismissRules = parseDismissString(dismissControl);
-        if (dismissRules.length <= index) {
+        // Check if it's already manually dismissed
+        final boolean isDismissed = mSharedPrefs.getBoolean(keyBase + IS_DISMISSED, false);
+        if (isDismissed) {
             return true;
         }
-        int currentDismiss = dismissRules[index];
-        long time = getEndTime(mSharedPrefs.getLong(keyBase + SETUP_TIME, 0), currentDismiss);
-        if (System.currentTimeMillis() >= time) {
+        if (dismissControl == null) {
+            return false;
+        }
+        // Parse when suggestion should first appear. return true to artificially hide suggestion
+        // before then.
+        int firstAppearDay = parseDismissString(dismissControl);
+        long firstAppearDayInMs = getEndTime(mSharedPrefs.getLong(keyBase + SETUP_TIME, 0),
+                firstAppearDay);
+        if (System.currentTimeMillis() >= firstAppearDayInMs) {
             // Dismiss timeout has passed, undismiss it.
             mSharedPrefs.edit()
                     .putBoolean(keyBase + IS_DISMISSED, false)
-                    .putInt(keyBase + DISMISS_INDEX, index + 1)
                     .commit();
             return false;
         }
@@ -394,18 +379,18 @@
         return startTime + days;
     }
 
-    private int[] parseDismissString(String dismissControl) {
-        String[] dismissStrs = dismissControl.split(",");
-        int[] dismisses = new int[dismissStrs.length];
-        for (int i = 0; i < dismissStrs.length; i++) {
-            dismisses[i] = Integer.parseInt(dismissStrs[i]);
-        }
-        return dismisses;
+    /**
+     * Parse the first int from a string formatted as "0,1,2..."
+     * The value means suggestion should first appear on Day X.
+     */
+    private int parseDismissString(String dismissControl) {
+        final String[] dismissStrs = dismissControl.split(",");
+        return Integer.parseInt(dismissStrs[0]);
     }
 
     private String getDismissControl(Tile suggestion, boolean isSmartSuggestionEnabled) {
         if (isSmartSuggestionEnabled) {
-            return mSmartDismissControl;
+            return mDefaultDismissControl;
         } else {
             return suggestion.metaData.getString(META_DATA_DISMISS_CONTROL);
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 33af4039..b8c5aca 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -254,6 +254,8 @@
             mCarrierName = savedState.getString(KEY_CARRIER_NAME);
         }
         update(mConfig, mInfo, mNetworkInfo);
+
+        // Do not evict old scan results on initial creation
         updateRssi();
         updateSeen();
         mId = sLastId.incrementAndGet();
@@ -495,10 +497,6 @@
     }
 
     private void evictOldScanResults() {
-        if (WifiTracker.sStaleScanResults) {
-            // Do not evict old scan results unless we are scanning and have fresh results.
-            return;
-        }
         long nowMs = SystemClock.elapsedRealtime();
         for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) {
             ScanResult result = iter.next();
@@ -562,12 +560,8 @@
      * results, returning the best RSSI for all matching AccessPoints averaged with the previous
      * value. If the access point is not connected and there are no scan results, the rssi will be
      * set to {@link #UNREACHABLE_RSSI}.
-     *
-     * <p>Old scan results will be evicted from the cache when this method is invoked.
      */
     private void updateRssi() {
-        evictOldScanResults();
-
         if (this.isActive()) {
             return;
         }
@@ -586,14 +580,8 @@
         }
     }
 
-    /**
-     * Updates {@link #mSeen} based on the scan result cache.
-     *
-     * <p>Old scan results will be evicted from the cache when this method is invoked.
-     */
+    /** Updates {@link #mSeen} based on the scan result cache. */
     private void updateSeen() {
-        evictOldScanResults();
-
         // TODO(sghuman): Set to now if connected
 
         long seen = 0;
@@ -1097,12 +1085,22 @@
         mAccessPointListener = listener;
     }
 
-    boolean update(ScanResult result) {
+    /**
+     * Update the AP with the given scan result.
+     *
+     * @param result the ScanResult to add to the AccessPoint scan cache
+     * @param evictOldScanResults whether stale scan results should be removed
+     *         from the cache during this update process
+     * @return true if the scan result update caused a change in state which would impact ranking
+     *     or AccessPoint rendering (e.g. wifi level, security)
+     */
+    boolean update(ScanResult result, boolean evictOldScanResults) {
         if (matches(result)) {
             int oldLevel = getLevel();
 
             /* Add or update the scan result for the BSSID */
             mScanResultCache.put(result.BSSID, result);
+            if (evictOldScanResults) evictOldScanResults();
             updateSeen();
             updateRssi();
             int newLevel = getLevel();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 45032ce..a242570 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -147,7 +147,7 @@
     Scanner mScanner;
 
     @GuardedBy("mLock")
-    static boolean sStaleScanResults = true;
+    private boolean mStaleScanResults = true;
 
     public WifiTracker(Context context, WifiListener wifiListener,
             boolean includeSaved, boolean includeScans) {
@@ -356,7 +356,7 @@
      * <p>This should always be called when done with a WifiTracker (if startTracking was called) to
      * ensure proper cleanup and prevent any further callbacks from occurring.
      *
-     * <p>Calling this method will set the {@link #sStaleScanResults} bit, which prevents
+     * <p>Calling this method will set the {@link #mStaleScanResults} bit, which prevents
      * {@link WifiListener#onAccessPointsChanged()} callbacks from being invoked (until the bit
      * is unset on the next SCAN_RESULTS_AVAILABLE_ACTION).
      */
@@ -374,7 +374,7 @@
 
             mWorkHandler.removePendingMessages();
             mMainHandler.removePendingMessages();
-            sStaleScanResults = true;
+            mStaleScanResults = true;
         }
     }
 
@@ -480,7 +480,7 @@
     /**
      * Safely modify {@link #mInternalAccessPoints} by acquiring {@link #mLock} first.
      *
-     * <p>Will not perform the update if {@link #sStaleScanResults} is true
+     * <p>Will not perform the update if {@link #mStaleScanResults} is true
      */
     private void updateAccessPoints() {
         List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
@@ -490,7 +490,7 @@
         }
 
         synchronized (mLock) {
-            if(!sStaleScanResults) {
+            if(!mStaleScanResults) {
                 updateAccessPointsLocked(newScanResults, configs);
             }
         }
@@ -500,7 +500,7 @@
      * Update the internal list of access points.
      *
      * <p>Do not called directly (except for forceUpdate), use {@link #updateAccessPoints()} which
-     * respects {@link #sStaleScanResults}.
+     * respects {@link #mStaleScanResults}.
      */
     @GuardedBy("mLock")
     private void updateAccessPointsLocked(final List<ScanResult> newScanResults,
@@ -574,7 +574,8 @@
 
                 boolean found = false;
                 for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
-                    if (accessPoint.update(result)) {
+                    // We want to evict old scan results if are current results are not stale
+                    if (accessPoint.update(result, !mStaleScanResults)) {
                         found = true;
                         break;
                     }
@@ -647,7 +648,8 @@
         for (int i = 0; i < N; i++) {
             if (cache.get(i).matches(result)) {
                 AccessPoint ret = cache.remove(i);
-                ret.update(result);
+                // evict old scan results only if we have fresh results
+                ret.update(result, !mStaleScanResults);
                 return ret;
             }
         }
@@ -847,7 +849,7 @@
                     // Only notify listeners of changes if we have fresh scan results, otherwise the
                     // UI will be updated with stale results. We want to copy the APs regardless,
                     // for instances where forceUpdate was invoked by the caller.
-                    if (sStaleScanResults) {
+                    if (mStaleScanResults) {
                         copyAndNotifyListeners(false /*notifyListeners*/);
                     } else {
                         copyAndNotifyListeners(true /*notifyListeners*/);
@@ -902,7 +904,7 @@
             switch (msg.what) {
                 case MSG_UPDATE_ACCESS_POINTS:
                     if (msg.arg1 == CLEAR_STALE_SCAN_RESULTS) {
-                        sStaleScanResults = false;
+                        mStaleScanResults = false;
                     }
                     updateAccessPoints();
                     break;
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 24f0c7a..ae59d37 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
@@ -258,7 +258,7 @@
         scanResult.BSSID = "bssid";
         scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
         scanResult.capabilities = "";
-        assertThat(ap.update(scanResult)).isTrue();
+        assertThat(ap.update(scanResult, true /* evict old scan results */)).isTrue();
 
         assertThat(ap.getRssi()).isEqualTo(expectedRssi);
     }
@@ -554,7 +554,7 @@
         scanResult.isCarrierAp = true;
         scanResult.carrierApEapType = carrierApEapType;
         scanResult.carrierName = carrierName;
-        assertThat(ap.update(scanResult)).isTrue();
+        assertThat(ap.update(scanResult, true /* evictOldScanresults */)).isTrue();
 
         assertThat(ap.isCarrierAp()).isEqualTo(true);
         assertThat(ap.getCarrierApEapType()).isEqualTo(carrierApEapType);
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 76d9823..df6587e 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
@@ -137,7 +137,6 @@
     private Looper mMainLooper;
 
     private int mOriginalScoringUiSettingValue;
-    private boolean mOriginalStaleScanResultsValue;
 
     @Before
     public void setUp() {
@@ -213,7 +212,6 @@
                 Settings.Global.NETWORK_SCORING_UI_ENABLED,
                 1 /* enabled */);
 
-        mOriginalStaleScanResultsValue = WifiTracker.sStaleScanResults;
     }
 
     @After
@@ -222,8 +220,6 @@
                 InstrumentationRegistry.getTargetContext().getContentResolver(),
                 Settings.Global.NETWORK_SCORING_UI_ENABLED,
                 mOriginalScoringUiSettingValue);
-
-        WifiTracker.sStaleScanResults = mOriginalStaleScanResultsValue;
     }
 
     private static ScanResult buildScanResult1() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
index 8391136..f6404a2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
@@ -83,7 +83,7 @@
 
         mSuggestionParser = new SuggestionParser(mContext, mPrefs,
                 Arrays.asList(mMultipleCategory, mExclusiveCategory, mExpiredExclusiveCategory),
-                "0,0");
+                "0");
 
         ResolveInfo info1 = TileUtilsTest.newInfo(true, null);
         info1.activityInfo.packageName = "pkg";
@@ -109,17 +109,12 @@
     }
 
     @Test
-    public void testDismissSuggestion_withoutSmartSuggestion() {
-        assertThat(mSuggestionParser.dismissSuggestion(mSuggestion, false)).isTrue();
+    public void dismissSuggestion_shouldDismiss() {
+        assertThat(mSuggestionParser.dismissSuggestion(mSuggestion)).isTrue();
     }
 
     @Test
-    public void testDismissSuggestion_withSmartSuggestion() {
-        assertThat(mSuggestionParser.dismissSuggestion(mSuggestion, true)).isFalse();
-    }
-
-    @Test
-    public void testGetSuggestions_withoutSmartSuggestions() {
+    public void testGetSuggestions_withoutSmartSuggestions_shouldDismiss() {
         readAndDismissSuggestion(false);
         mSuggestionParser.readSuggestions(mMultipleCategory, mSuggestionsAfterDismiss, false);
         assertThat(mSuggestionsBeforeDismiss).hasSize(2);
@@ -128,11 +123,10 @@
     }
 
     @Test
-    public void testGetSuggestions_withSmartSuggestions() {
+    public void testGetSuggestions_withSmartSuggestions_shouldDismiss() {
         readAndDismissSuggestion(true);
         assertThat(mSuggestionsBeforeDismiss).hasSize(2);
-        assertThat(mSuggestionsAfterDismiss).hasSize(2);
-        assertThat(mSuggestionsBeforeDismiss).isEqualTo(mSuggestionsAfterDismiss);
+        assertThat(mSuggestionsAfterDismiss).hasSize(1);
     }
 
     @Test
@@ -191,19 +185,15 @@
     }
 
     @Test
-    public void isSuggestionDismissed_mismatchRule_shouldDismiss() {
+    public void isSuggestionDismissed_dismissedSuggestion_shouldReturnTrue() {
         final Tile suggestion = new Tile();
         suggestion.metaData = new Bundle();
         suggestion.metaData.putString(SuggestionParser.META_DATA_DISMISS_CONTROL, "1,2,3");
         suggestion.intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
 
         // Dismiss suggestion when smart suggestion is not enabled.
-        mSuggestionParser.dismissSuggestion(suggestion, false /* isSmartSuggestionEnabled */);
-        final String suggestionKey = suggestion.intent.getComponent().flattenToShortString();
-        // And point to last rule in dismiss control
-        mPrefs.edit().putInt(suggestionKey + SuggestionParser.DISMISS_INDEX, 2).apply();
+        mSuggestionParser.dismissSuggestion(suggestion);
 
-        // Turn on smart suggestion, and check if suggestion is enabled.
         assertThat(mSuggestionParser.isDismissed(suggestion, true /* isSmartSuggestionEnabled */))
                 .isTrue();
     }
@@ -215,7 +205,7 @@
                 mMultipleCategory, mSuggestionsBeforeDismiss, isSmartSuggestionEnabled);
 
         final Tile suggestion = mSuggestionsBeforeDismiss.get(0);
-        if (mSuggestionParser.dismissSuggestion(suggestion, isSmartSuggestionEnabled)) {
+        if (mSuggestionParser.dismissSuggestion(suggestion)) {
             RuntimeEnvironment.getRobolectricPackageManager().removeResolveInfosForIntent(
                     new Intent(Intent.ACTION_MAIN).addCategory(mMultipleCategory.category),
                     suggestion.intent.getComponent().getPackageName());
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java
index bb21fb3..1f633da 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java
@@ -26,6 +26,8 @@
     int VERSION = 1;
 
     void showGlobalActions(GlobalActionsManager manager);
+    default void showShutdownUi(boolean isReboot, String reason) {
+    }
 
     @ProvidesInterface(version = GlobalActionsManager.VERSION)
     public interface GlobalActionsManager {
diff --git a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
index 37e2402..5d31678 100644
--- a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
+++ b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
@@ -24,6 +24,7 @@
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
@@ -69,12 +70,14 @@
         mOverlay = LayoutInflater.from(mContext)
                 .inflate(R.layout.rounded_corners, null);
         mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+        mOverlay.setAlpha(0);
         mOverlay.findViewById(R.id.right).setRotation(90);
 
         mContext.getSystemService(WindowManager.class)
                 .addView(mOverlay, getWindowLayoutParams());
         mBottomOverlay = LayoutInflater.from(mContext)
                 .inflate(R.layout.rounded_corners, null);
+        mBottomOverlay.setAlpha(0);
         mBottomOverlay.findViewById(R.id.right).setRotation(180);
         mBottomOverlay.findViewById(R.id.left).setRotation(270);
         WindowManager.LayoutParams layoutParams = getWindowLayoutParams();
@@ -88,6 +91,23 @@
         mDensity = metrics.density;
 
         Dependency.get(TunerService.class).addTunable(this, SIZE);
+
+        mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft,
+                    int oldTop, int oldRight, int oldBottom) {
+                mOverlay.removeOnLayoutChangeListener(this);
+                mOverlay.animate()
+                        .alpha(1)
+                        .setDuration(1000)
+                        .start();
+                mBottomOverlay.animate()
+                        .alpha(1)
+                        .setDuration(1000)
+                        .start();
+            }
+        });
     }
 
     private void setupPadding(int padding) {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index f07027e..09a08f0 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -46,6 +46,11 @@
     }
 
     @Override
+    public void handleShowShutdownUi(boolean isReboot, String reason) {
+        mExtension.get().showShutdownUi(isReboot, reason);
+    }
+
+    @Override
     public void handleShowGlobalActionsMenu() {
         mExtension.get().showGlobalActions(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 7799c01..33d5617 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -116,8 +116,6 @@
     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;
@@ -682,10 +680,7 @@
     /** {@inheritDoc} */
     public void onClick(DialogInterface dialog, int which) {
         Action item = mAdapter.getItem(which);
-        if ((item instanceof PowerAction)
-                || (item instanceof RestartAction)) {
-            if (mDialog != null) mDialog.fadeOut();
-        } else if (!(item instanceof SilentModeTriStateAction)) {
+        if (!(item instanceof SilentModeTriStateAction)) {
             dialog.dismiss();
         }
         item.onPress();
@@ -1325,23 +1320,6 @@
                     .start();
         }
 
-        public void fadeOut() {
-            mHardwareLayout.setTranslationX(0);
-            mHardwareLayout.setAlpha(1);
-            mListView.animate()
-                    .alpha(0)
-                    .translationX(getAnimTranslation())
-                    .setDuration(300)
-                    .setInterpolator(new LogAccelerateInterpolator())
-                    .setUpdateListener(animation -> {
-                        float frac = animation.getAnimatedFraction();
-                        float alpha = NotificationUtils.interpolate(
-                                ScrimController.GRADIENT_SCRIM_ALPHA, SHUTDOWN_SCRIM_ALPHA, frac);
-                        mGradientDrawable.setAlpha((int) (alpha * 255));
-                    })
-                    .start();
-        }
-
         private float getAnimTranslation() {
             return getContext().getResources().getDimension(
                     com.android.systemui.R.dimen.global_actions_panel_width) / 2;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 08b7b71..2cf230c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -14,17 +14,33 @@
 
 package com.android.systemui.globalactions;
 
+import android.app.Dialog;
+import android.app.KeyguardManager;
+import android.app.WallpaperColors;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.colorextraction.ColorExtractor.GradientColors;
+import com.android.internal.colorextraction.drawable.GradientDrawable;
+import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
-import com.android.systemui.R;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.GlobalActions;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 
-import android.content.Context;
-import android.support.v7.view.ContextThemeWrapper;
-
 public class GlobalActionsImpl implements GlobalActions {
 
+    private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f;
+
     private final Context mContext;
     private final KeyguardMonitor mKeyguardMonitor;
     private final DeviceProvisionedController mDeviceProvisionedController;
@@ -44,4 +60,51 @@
         mGlobalActions.showDialog(mKeyguardMonitor.isShowing(),
                 mDeviceProvisionedController.isDeviceProvisioned());
     }
+
+    @Override
+    public void showShutdownUi(boolean isReboot, String reason) {
+        GradientDrawable background = new GradientDrawable(mContext);
+        background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255));
+
+        Dialog d = new Dialog(mContext,
+                com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
+        // Window initialization
+        Window window = d.getWindow();
+        window.getAttributes().width = ViewGroup.LayoutParams.MATCH_PARENT;
+        window.getAttributes().height = ViewGroup.LayoutParams.MATCH_PARENT;
+        window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+        window.requestFeature(Window.FEATURE_NO_TITLE);
+        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
+                | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
+        window.addFlags(
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+        window.setBackgroundDrawable(background);
+        window.setWindowAnimations(R.style.Animation_Toast);
+
+        d.setContentView(R.layout.shutdown_dialog);
+        d.setCancelable(false);
+
+        int color = Utils.getColorAttr(mContext, com.android.systemui.R.attr.wallpaperTextColor);
+        boolean onKeyguard = mContext.getSystemService(
+                KeyguardManager.class).isKeyguardLocked();
+
+        ProgressBar bar = d.findViewById(R.id.progress);
+        bar.getIndeterminateDrawable().setTint(color);
+        TextView message = d.findViewById(R.id.text1);
+        message.setTextColor(color);
+        if (isReboot) message.setText(R.string.reboot_to_reset_message);
+
+        Point displaySize = new Point();
+        mContext.getDisplay().getRealSize(displaySize);
+        GradientColors colors = Dependency.get(SysuiColorExtractor.class).getColors(
+                onKeyguard ? WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM);
+        background.setColors(colors, false);
+        background.setScreenSize(displaySize.x, displaySize.y);
+
+        d.show();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 2e10831..bbf9eb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -80,6 +80,7 @@
     private static final int MSG_HANDLE_SYSTEM_KEY             = 33 << MSG_SHIFT;
     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;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -138,6 +139,7 @@
 
         default void handleSystemKey(int arg1) { }
         default void handleShowGlobalActionsMenu() { }
+        default void handleShowShutdownUi(boolean isReboot, String reason) { }
     }
 
     @VisibleForTesting
@@ -438,6 +440,15 @@
         }
     }
 
+    @Override
+    public void showShutdownUi(boolean isReboot, String reason) {
+        synchronized (mLock) {
+            mHandler.removeMessages(MSG_SHOW_SHUTDOWN_UI);
+            mHandler.obtainMessage(MSG_SHOW_SHUTDOWN_UI, isReboot ? 1 : 0, 0, reason)
+                    .sendToTarget();
+        }
+    }
+
     private final class H extends Handler {
         private H(Looper l) {
             super(l);
@@ -624,6 +635,11 @@
                         mCallbacks.get(i).handleShowGlobalActionsMenu();
                     }
                     break;
+                case MSG_SHOW_SHUTDOWN_UI:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).handleShowShutdownUi(msg.arg1 != 0, (String) msg.obj);
+                    }
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 881de67..a6691b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -50,6 +50,7 @@
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
+import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 
 /**
@@ -346,6 +347,9 @@
         applyDarkness(R.id.signal_cluster, tintArea, intensity, iconColor);
         applyDarkness(R.id.battery, tintArea, intensity, iconColor);
         applyDarkness(R.id.clock, tintArea, intensity, iconColor);
+        // Reload user avatar
+        ((UserInfoControllerImpl) Dependency.get(UserInfoController.class))
+                .onDensityOrFontScaleChanged();
     }
 
     private void applyDarkness(int id, Rect tintArea, float intensity, int color) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
index b1e4b03..527addf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java
@@ -131,6 +131,7 @@
         final int userId = userInfo.id;
         final boolean isGuest = userInfo.isGuest();
         final String userName = userInfo.name;
+        final boolean lightIcon = mContext.getThemeResId() != R.style.Theme_SystemUI_Light;
 
         final Resources res = mContext.getResources();
         final int avatarSize = Math.max(
@@ -154,7 +155,7 @@
                             .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake();
                 } else {
                     avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId,
-                            /* light= */ true);
+                            lightIcon);
                 }
 
                 // If it's a single-user device, get the profile name, since the nickname is not
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 28e3427..c4dc506 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -276,6 +276,42 @@
 
   // Count of saved Passpoint providers device has ever connected to.
   optional int32 num_passpoint_providers_successfully_connected = 63;
+
+  // Histogram counting instances of scans with N many ScanResults with unique ssids
+  repeated NumConnectableNetworksBucket total_ssids_in_scan_histogram = 64;
+
+  // Histogram counting instances of scans with N many ScanResults/bssids
+  repeated NumConnectableNetworksBucket total_bssids_in_scan_histogram = 65;
+
+  // Histogram counting instances of scans with N many unique open ssids
+  repeated NumConnectableNetworksBucket available_open_ssids_in_scan_histogram = 66;
+
+  // Histogram counting instances of scans with N many bssids for open networks
+  repeated NumConnectableNetworksBucket available_open_bssids_in_scan_histogram = 67;
+
+  // Histogram counting instances of scans with N many unique ssids for saved networks
+  repeated NumConnectableNetworksBucket available_saved_ssids_in_scan_histogram = 68;
+
+  // Histogram counting instances of scans with N many bssids for saved networks
+  repeated NumConnectableNetworksBucket available_saved_bssids_in_scan_histogram = 69;
+
+  // Histogram counting instances of scans with N many unique SSIDs for open or saved networks
+  repeated NumConnectableNetworksBucket available_open_or_saved_ssids_in_scan_histogram = 70;
+
+  // Histogram counting instances of scans with N many BSSIDs for open or saved networks
+  repeated NumConnectableNetworksBucket available_open_or_saved_bssids_in_scan_histogram = 71;
+
+  // Histogram counting instances of scans with N many ScanResults matching unique saved passpoint providers
+  repeated NumConnectableNetworksBucket available_saved_passpoint_provider_profiles_in_scan_histogram = 72;
+
+  // Histogram counting instances of scans with N many ScanResults BSSIDs matching a saved passpoint provider
+  repeated NumConnectableNetworksBucket available_saved_passpoint_provider_bssids_in_scan_histogram = 73;
+
+  // Counts the number of AllSingleScanLister.onResult calls with a full band scan result
+  optional int32 full_band_all_single_scan_listener_results = 74;
+
+  // Counts the number of AllSingleScanLister.onResult calls with a partial (channels) scan result
+  optional int32 partial_all_single_scan_listener_results = 75;
 }
 
 // Information that gets logged for every WiFi connection.
@@ -883,3 +919,11 @@
   }
 }
 
+// Data point used to build 'Number of Connectable Network' histograms
+message NumConnectableNetworksBucket {
+  // Number of connectable networks seen in a scan result
+  optional int32 num_connectable_networks = 1 [default = 0];
+
+  // Number of scan results with num_connectable_networks
+  optional int32 count = 2 [default = 0];
+}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d47ca1c..cb4becc 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1448,52 +1448,23 @@
         // rebooted in the middle of an operation that was removing something from
         // this log, we sanity-check its contents here and reconstruct it.
         mEverStored = new File(mBaseStateDir, "processed");
-        File tempProcessedFile = new File(mBaseStateDir, "processed.new");
-
-        // If we were in the middle of removing something from the ever-backed-up
-        // file, there might be a transient "processed.new" file still present.
-        // Ignore it -- we'll validate "processed" against the current package set.
-        if (tempProcessedFile.exists()) {
-            tempProcessedFile.delete();
-        }
 
         // If there are previous contents, parse them out then start a new
         // file to continue the recordkeeping.
         if (mEverStored.exists()) {
-            DataOutputStream temp = null;
-            DataInputStream in = null;
-
-            try {
-                temp = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(
-                        tempProcessedFile)));
-                in = new DataInputStream(new BufferedInputStream(new FileInputStream(mEverStored)));
+            try (DataInputStream in = new DataInputStream(
+                    new BufferedInputStream(new FileInputStream(mEverStored)))) {
 
                 // Loop until we hit EOF
                 while (true) {
                     String pkg = in.readUTF();
-                    try {
-                        // is this package still present?
-                        mPackageManager.getPackageInfo(pkg, 0);
-                        // if we get here then yes it is; remember it
-                        mEverStoredApps.add(pkg);
-                        temp.writeUTF(pkg);
-                        if (MORE_DEBUG) Slog.v(TAG, "   + " + pkg);
-                    } catch (NameNotFoundException e) {
-                        // nope, this package was uninstalled; don't include it
-                        if (MORE_DEBUG) Slog.v(TAG, "   - " + pkg);
-                    }
+                    mEverStoredApps.add(pkg);
+                    if (MORE_DEBUG) Slog.v(TAG, "   + " + pkg);
                 }
             } catch (EOFException e) {
-                // Once we've rewritten the backup history log, atomically replace the
-                // old one with the new one then reopen the file for continuing use.
-                if (!tempProcessedFile.renameTo(mEverStored)) {
-                    Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
-                }
+                // Done
             } catch (IOException e) {
                 Slog.e(TAG, "Error in processed file", e);
-            } finally {
-                try { if (temp != null) temp.close(); } catch (IOException e) {}
-                try { if (in != null) in.close(); } catch (IOException e) {}
             }
         }
 
@@ -2240,44 +2211,6 @@
         }
     }
 
-    // Remove our awareness of having ever backed up the given package
-    void removeEverBackedUp(String packageName) {
-        if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName);
-        if (MORE_DEBUG) Slog.v(TAG, "New set:");
-
-        synchronized (mEverStoredApps) {
-            // Rewrite the file and rename to overwrite.  If we reboot in the middle,
-            // we'll recognize on initialization time that the package no longer
-            // exists and fix it up then.
-            File tempKnownFile = new File(mBaseStateDir, "processed.new");
-            RandomAccessFile known = null;
-            try {
-                known = new RandomAccessFile(tempKnownFile, "rws");
-                mEverStoredApps.remove(packageName);
-                for (String s : mEverStoredApps) {
-                    known.writeUTF(s);
-                    if (MORE_DEBUG) Slog.v(TAG, "    " + s);
-                }
-                known.close();
-                known = null;
-                if (!tempKnownFile.renameTo(mEverStored)) {
-                    throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
-                }
-            } catch (IOException e) {
-                // Bad: we couldn't create the new copy.  For safety's sake we
-                // abandon the whole process and remove all what's-backed-up
-                // state entirely, meaning we'll force a backup pass for every
-                // participant on the next boot or [re]install.
-                Slog.w(TAG, "Error rewriting " + mEverStored, e);
-                mEverStoredApps.clear();
-                tempKnownFile.delete();
-                mEverStored.delete();
-            } finally {
-                try { if (known != null) known.close(); } catch (IOException e) {}
-            }
-        }
-    }
-
     // Persistently record the current and ancestral backup tokens as well
     // as the set of packages with data [supposedly] available in the
     // ancestral dataset.
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index 13e3ae6..c297010 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -370,7 +370,7 @@
 
     private void unbindServiceLocked() {
         for (SpellCheckerBindGroup scbg : mSpellCheckerBindGroups.values()) {
-            scbg.removeAll();
+            scbg.removeAllLocked();
         }
         mSpellCheckerBindGroups.clear();
     }
@@ -788,29 +788,41 @@
             mListeners = new InternalDeathRecipients(this);
         }
 
-        public void onServiceConnected(ISpellCheckerService spellChecker) {
+        public void onServiceConnectedLocked(ISpellCheckerService spellChecker) {
             if (DBG) {
-                Slog.d(TAG, "onServiceConnected");
+                Slog.d(TAG, "onServiceConnectedLocked");
             }
 
-            synchronized (mLock) {
-                mSpellChecker = spellChecker;
-                mConnected = true;
-                // Dispatch pending getISpellCheckerSession requests.
-                mPendingSessionRequests.forEach(this::getISpellCheckerSessionLocked);
-                mPendingSessionRequests.clear();
+            if (mUnbindCalled) {
+                return;
             }
+            mSpellChecker = spellChecker;
+            mConnected = true;
+            // Dispatch pending getISpellCheckerSession requests.
+            try {
+                final int size = mPendingSessionRequests.size();
+                for (int i = 0; i < size; ++i) {
+                    final SessionRequest request = mPendingSessionRequests.get(i);
+                    mSpellChecker.getISpellCheckerSession(
+                            request.mLocale, request.mScListener, request.mBundle,
+                            new ISpellCheckerServiceCallbackBinder(this, request));
+                    mOnGoingSessionRequests.add(request);
+                }
+                mPendingSessionRequests.clear();
+            } catch(RemoteException e) {
+                // The target spell checker service is not available.  Better to reset the state.
+                removeAllLocked();
+            }
+            cleanLocked();
         }
 
-        public void onServiceDisconnected() {
+        public void onServiceDisconnectedLocked() {
             if (DBG) {
-                Slog.d(TAG, "onServiceDisconnected");
+                Slog.d(TAG, "onServiceDisconnectedLocked");
             }
 
-            synchronized (mLock) {
-                mSpellChecker = null;
-                mConnected = false;
-            }
+            mSpellChecker = null;
+            mConnected = false;
         }
 
         public void removeListener(ISpellCheckerSessionListener listener) {
@@ -853,17 +865,15 @@
             mUnbindCalled = true;
         }
 
-        public void removeAll() {
+        public void removeAllLocked() {
             Slog.e(TAG, "Remove the spell checker bind unexpectedly.");
-            synchronized (mLock) {
-                final int size = mListeners.getRegisteredCallbackCount();
-                for (int i = size - 1; i >= 0; --i) {
-                    mListeners.unregister(mListeners.getRegisteredCallbackItem(i));
-                }
-                mPendingSessionRequests.clear();
-                mOnGoingSessionRequests.clear();
-                cleanLocked();
+            final int size = mListeners.getRegisteredCallbackCount();
+            for (int i = size - 1; i >= 0; --i) {
+                mListeners.unregister(mListeners.getRegisteredCallbackItem(i));
             }
+            mPendingSessionRequests.clear();
+            mOnGoingSessionRequests.clear();
+            cleanLocked();
         }
 
         public void getISpellCheckerSessionOrQueueLocked(@NonNull SessionRequest request) {
@@ -874,13 +884,6 @@
                 mPendingSessionRequests.add(request);
                 return;
             }
-            getISpellCheckerSessionLocked(request);
-        }
-
-        private void getISpellCheckerSessionLocked(@NonNull SessionRequest request) {
-            if (mUnbindCalled) {
-                return;
-            }
             try {
                 mSpellChecker.getISpellCheckerSession(
                         request.mLocale, request.mScListener, request.mBundle,
@@ -888,7 +891,7 @@
                 mOnGoingSessionRequests.add(request);
             } catch(RemoteException e) {
                 // The target spell checker service is not available.  Better to reset the state.
-                removeAll();
+                removeAllLocked();
             }
             cleanLocked();
         }
@@ -930,13 +933,13 @@
 
         private void onServiceConnectedInnerLocked(ComponentName name, IBinder service) {
             if (DBG) {
-                Slog.w(TAG, "onServiceConnected: " + name);
+                Slog.w(TAG, "onServiceConnectedInnerLocked: " + name);
             }
             final ISpellCheckerService spellChecker =
                     ISpellCheckerService.Stub.asInterface(service);
             final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId);
             if (group != null && this == group.mInternalConnection) {
-                group.onServiceConnected(spellChecker);
+                group.onServiceConnectedLocked(spellChecker);
             }
         }
 
@@ -949,11 +952,11 @@
 
         private void onServiceDisconnectedInnerLocked(ComponentName name) {
             if (DBG) {
-                Slog.w(TAG, "onServiceDisconnected: " + name);
+                Slog.w(TAG, "onServiceDisconnectedInnerLocked: " + name);
             }
             final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId);
             if (group != null && this == group.mInternalConnection) {
-                group.onServiceDisconnected();
+                group.onServiceDisconnectedLocked();
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index e56b09d..74c4826 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -679,102 +679,111 @@
                 }
                 writeTaskIdsFiles();
 
-                // If mNextWriteTime, then don't delay between each call to saveToXml().
-                final WriteQueueItem item;
-                synchronized (TaskPersister.this) {
-                    if (mNextWriteTime != FLUSH_QUEUE) {
-                        // The next write we don't have to wait so long.
-                        mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
-                        if (DEBUG) Slog.d(TAG, "Next write time may be in " +
-                                INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
-                    }
+                processNextItem();
+            }
+        }
 
-                    while (mWriteQueue.isEmpty()) {
-                        if (mNextWriteTime != 0) {
-                            mNextWriteTime = 0; // idle.
-                            TaskPersister.this.notifyAll(); // wake up flush() if needed.
-                        }
-                        try {
-                            if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
-                            TaskPersister.this.wait();
-                        } catch (InterruptedException e) {
-                        }
-                        // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
-                        // from now.
-                    }
-                    item = mWriteQueue.remove(0);
+        private void processNextItem() {
+            // This part is extracted into a method so that the GC can clearly see the end of the
+            // scope of the variable 'item'.  If this part was in the loop above, the last item
+            // it processed would always "leak".
+            // See https://b.corp.google.com/issues/64438652#comment7
 
-                    long now = SystemClock.uptimeMillis();
-                    if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
-                            mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
-                    while (now < mNextWriteTime) {
-                        try {
-                            if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
-                                    (mNextWriteTime - now));
-                            TaskPersister.this.wait(mNextWriteTime - now);
-                        } catch (InterruptedException e) {
-                        }
-                        now = SystemClock.uptimeMillis();
-                    }
-
-                    // Got something to do.
+            // If mNextWriteTime, then don't delay between each call to saveToXml().
+            final WriteQueueItem item;
+            synchronized (TaskPersister.this) {
+                if (mNextWriteTime != FLUSH_QUEUE) {
+                    // The next write we don't have to wait so long.
+                    mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
+                    if (DEBUG) Slog.d(TAG, "Next write time may be in " +
+                            INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
                 }
 
-                if (item instanceof ImageWriteQueueItem) {
-                    ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
-                    final String filePath = imageWriteQueueItem.mFilePath;
-                    if (!createParentDirectory(filePath)) {
-                        Slog.e(TAG, "Error while creating images directory for file: " + filePath);
-                        continue;
+                while (mWriteQueue.isEmpty()) {
+                    if (mNextWriteTime != 0) {
+                        mNextWriteTime = 0; // idle.
+                        TaskPersister.this.notifyAll(); // wake up flush() if needed.
                     }
-                    final Bitmap bitmap = imageWriteQueueItem.mImage;
-                    if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
-                    FileOutputStream imageFile = null;
                     try {
-                        imageFile = new FileOutputStream(new File(filePath));
-                        bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
-                    } catch (Exception e) {
-                        Slog.e(TAG, "saveImage: unable to save " + filePath, e);
-                    } finally {
-                        IoUtils.closeQuietly(imageFile);
+                        if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
+                        TaskPersister.this.wait();
+                    } catch (InterruptedException e) {
                     }
-                } else if (item instanceof TaskWriteQueueItem) {
-                    // Write out one task.
-                    StringWriter stringWriter = null;
-                    TaskRecord task = ((TaskWriteQueueItem) item).mTask;
-                    if (DEBUG) Slog.d(TAG, "Writing task=" + task);
-                    synchronized (mService) {
-                        if (task.inRecents) {
-                            // Still there.
-                            try {
-                                if (DEBUG) Slog.d(TAG, "Saving task=" + task);
-                                stringWriter = saveToXml(task);
-                            } catch (IOException e) {
-                            } catch (XmlPullParserException e) {
-                            }
-                        }
+                    // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
+                    // from now.
+                }
+                item = mWriteQueue.remove(0);
+
+                long now = SystemClock.uptimeMillis();
+                if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
+                        mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
+                while (now < mNextWriteTime) {
+                    try {
+                        if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
+                                (mNextWriteTime - now));
+                        TaskPersister.this.wait(mNextWriteTime - now);
+                    } catch (InterruptedException e) {
                     }
-                    if (stringWriter != null) {
-                        // Write out xml file while not holding mService lock.
-                        FileOutputStream file = null;
-                        AtomicFile atomicFile = null;
+                    now = SystemClock.uptimeMillis();
+                }
+
+                // Got something to do.
+            }
+
+            if (item instanceof ImageWriteQueueItem) {
+                ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
+                final String filePath = imageWriteQueueItem.mFilePath;
+                if (!createParentDirectory(filePath)) {
+                    Slog.e(TAG, "Error while creating images directory for file: " + filePath);
+                    return;
+                }
+                final Bitmap bitmap = imageWriteQueueItem.mImage;
+                if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
+                FileOutputStream imageFile = null;
+                try {
+                    imageFile = new FileOutputStream(new File(filePath));
+                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
+                } catch (Exception e) {
+                    Slog.e(TAG, "saveImage: unable to save " + filePath, e);
+                } finally {
+                    IoUtils.closeQuietly(imageFile);
+                }
+            } else if (item instanceof TaskWriteQueueItem) {
+                // Write out one task.
+                StringWriter stringWriter = null;
+                TaskRecord task = ((TaskWriteQueueItem) item).mTask;
+                if (DEBUG) Slog.d(TAG, "Writing task=" + task);
+                synchronized (mService) {
+                    if (task.inRecents) {
+                        // Still there.
                         try {
-                            atomicFile = new AtomicFile(new File(
-                                    getUserTasksDir(task.userId),
-                                    String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
-                            file = atomicFile.startWrite();
-                            file.write(stringWriter.toString().getBytes());
-                            file.write('\n');
-                            atomicFile.finishWrite(file);
+                            if (DEBUG) Slog.d(TAG, "Saving task=" + task);
+                            stringWriter = saveToXml(task);
                         } catch (IOException e) {
-                            if (file != null) {
-                                atomicFile.failWrite(file);
-                            }
-                            Slog.e(TAG,
-                                    "Unable to open " + atomicFile + " for persisting. " + e);
+                        } catch (XmlPullParserException e) {
                         }
                     }
                 }
+                if (stringWriter != null) {
+                    // Write out xml file while not holding mService lock.
+                    FileOutputStream file = null;
+                    AtomicFile atomicFile = null;
+                    try {
+                        atomicFile = new AtomicFile(new File(
+                                getUserTasksDir(task.userId),
+                                String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
+                        file = atomicFile.startWrite();
+                        file.write(stringWriter.toString().getBytes());
+                        file.write('\n');
+                        atomicFile.finishWrite(file);
+                    } catch (IOException e) {
+                        if (file != null) {
+                            atomicFile.failWrite(file);
+                        }
+                        Slog.e(TAG,
+                                "Unable to open " + atomicFile + " for persisting. " + e);
+                    }
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index b0be8f7..015f7f0 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -832,20 +832,23 @@
             //       functions are ready to use.
             //
             // For more explanation, see b/62552150 .
-            if (usbConnected && !usbConfigured) {
-                // Nothing for us to do here.
-                // TODO: consider ignoring DISCONNECTED broadcasts as well.
-                return;
-            }
-
             synchronized (Tethering.this.mPublicSync) {
+                // Always record the state of RNDIS.
                 mRndisEnabled = rndisEnabled;
+
+                if (usbConnected && !usbConfigured) {
+                    // Nothing to do here (only CONNECTED, not yet CONFIGURED).
+                    return;
+                }
+
                 // start tethering if we have a request pending
                 if (usbConfigured && mRndisEnabled && mUsbTetherRequested) {
                     tetherMatchingInterfaces(
                             IControlsTethering.STATE_TETHERED,
                             ConnectivityManager.TETHERING_USB);
                 }
+
+                // TODO: Figure out how to remove the need for this variable.
                 mUsbTetherRequested = false;
             }
         }
diff --git a/services/core/java/com/android/server/fingerprint/EnumerateClient.java b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
index 12827d0..b6bbd1b 100644
--- a/services/core/java/com/android/server/fingerprint/EnumerateClient.java
+++ b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
@@ -95,7 +95,7 @@
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed to notify enumerated:", e);
         }
-        return fingerId == 0; // done when id hits 0
+        return remaining == 0;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java b/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
index f4d2596..88d9ef4 100644
--- a/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
@@ -88,7 +88,7 @@
             doFingerprintCleanup();
         }
 
-        return fingerId == 0; // done when id hits 0
+        return remaining == 0;
     }
 
 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a105c84..2f166e9 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1223,6 +1223,7 @@
                                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                                 .setUserAuthenticationRequired(true)
                                 .setUserAuthenticationValidityDurationSeconds(30)
+                                .setCriticalToDeviceEncryption(true)
                                 .build());
                 // Key imported, obtain a reference to it.
                 SecretKey keyStoreEncryptionKey = (SecretKey) keyStore.getKey(
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 603e46a..0e4b08f 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -625,8 +625,8 @@
             PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
             byte[] pwdToken = computePasswordToken(userCredential, pwd);
 
-            GateKeeperResponse response = gatekeeper.verify(fakeUid(persistentData.userId),
-                    pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
+            GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
+                    0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
             return VerifyCredentialResponse.fromGateKeeperResponse(response);
         } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
             PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 241d76f..da6e26e 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -318,7 +318,7 @@
         optimizer.performDexOpt(pkg, libraryDependencies,
                 null /* ISAs */,
                 null /* CompilerStats.PackageStats */,
-                mPackageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName),
+                mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(pkg.packageName),
                 new DexoptOptions(pkg.packageName, compilationReason,
                         DexoptOptions.DEXOPT_BOOT_COMPLETE));
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index dabd35c..2f4f5ab 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -31,6 +31,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.DexoptUtils;
 import com.android.server.pm.dex.PackageDexUsage;
@@ -39,6 +40,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import dalvik.system.DexFile;
 
@@ -112,7 +114,7 @@
      */
     int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
             String[] instructionSets, CompilerStats.PackageStats packageStats,
-            boolean isUsedByOtherApps, DexoptOptions options) {
+            PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
         if (!canOptimizePackage(pkg)) {
             return DEX_OPT_SKIPPED;
         }
@@ -120,7 +122,7 @@
             final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
             try {
                 return performDexOptLI(pkg, sharedLibraries, instructionSets,
-                        packageStats, isUsedByOtherApps, options);
+                        packageStats, packageUseInfo, options);
             } finally {
                 releaseWakeLockLI(acquireTime);
             }
@@ -134,21 +136,13 @@
     @GuardedBy("mInstallLock")
     private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
             String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
-            boolean isUsedByOtherApps, DexoptOptions options) {
+            PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
         final String[] instructionSets = targetInstructionSets != null ?
                 targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
         final List<String> paths = pkg.getAllCodePaths();
         final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
 
-        final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
-                options.getCompilerFilter(), isUsedByOtherApps);
-        final boolean profileUpdated = options.isCheckForProfileUpdates() &&
-                isProfileUpdated(pkg, sharedGid, compilerFilter);
-
-        // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
-        final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());
-
         // Get the class loader context dependencies.
         // For each code path in the package, this array contains the class loader context that
         // needs to be passed to dexopt in order to ensure correct optimizations.
@@ -172,6 +166,17 @@
                 }
             }
 
+            final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
+                    || packageUseInfo.isUsedByOtherApps(path);
+            final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
+                options.getCompilerFilter(), isUsedByOtherApps);
+            final boolean profileUpdated = options.isCheckForProfileUpdates() &&
+                isProfileUpdated(pkg, sharedGid, compilerFilter);
+
+            // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
+            // flags.
+            final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());
+
             for (String dexCodeIsa : dexCodeInstructionSets) {
                 int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
                         profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
@@ -376,26 +381,60 @@
     /**
      * Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}.
      */
-    void dumpDexoptState(IndentingPrintWriter pw, PackageParser.Package pkg) {
+    void dumpDexoptState(IndentingPrintWriter pw, PackageParser.Package pkg,
+            PackageDexUsage.PackageUseInfo useInfo) {
         final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
 
         final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
 
-        for (String instructionSet : dexCodeInstructionSets) {
-             pw.println("Instruction Set: " + instructionSet);
-             pw.increaseIndent();
-             for (String path : paths) {
-                  String status = null;
-                  try {
-                      status = DexFile.getDexFileStatus(path, instructionSet);
-                  } catch (IOException ioe) {
-                      status = "[Exception]: " + ioe.getMessage();
-                  }
-                  pw.println("path: " + path);
-                  pw.println("status: " + status);
-             }
-             pw.decreaseIndent();
+        for (String path : paths) {
+            pw.println("path: " + path);
+            pw.increaseIndent();
+
+            for (String isa : dexCodeInstructionSets) {
+                String status = null;
+                try {
+                    status = DexFile.getDexFileStatus(path, isa);
+                } catch (IOException ioe) {
+                     status = "[Exception]: " + ioe.getMessage();
+                }
+                pw.println(isa + ": " + status);
+            }
+
+            if (useInfo.isUsedByOtherApps(path)) {
+                pw.println("used be other apps: " + useInfo.getLoadingPackages(path));
+            }
+
+            Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap();
+
+            if (!dexUseInfoMap.isEmpty()) {
+                pw.println("known secondary dex files:");
+                pw.increaseIndent();
+                for (Map.Entry<String, PackageDexUsage.DexUseInfo> e : dexUseInfoMap.entrySet()) {
+                    String dex = e.getKey();
+                    PackageDexUsage.DexUseInfo dexUseInfo = e.getValue();
+                    pw.println(dex);
+                    pw.increaseIndent();
+                    for (String isa : dexUseInfo.getLoaderIsas()) {
+                        String status = null;
+                        try {
+                            status = DexFile.getDexFileStatus(path, isa);
+                        } catch (IOException ioe) {
+                             status = "[Exception]: " + ioe.getMessage();
+                        }
+                        pw.println(isa + ": " + status);
+                    }
+
+                    pw.println("class loader context: " + dexUseInfo.getClassLoaderContext());
+                    if (dexUseInfo.isUsedByOtherApps()) {
+                        pw.println("used be other apps: " + dexUseInfo.getLoadingPackages());
+                    }
+                    pw.decreaseIndent();
+                }
+                pw.decreaseIndent();
+            }
+            pw.decreaseIndent();
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 0fd696f..d492590 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -831,7 +831,7 @@
 
             // Commit was keeping session marked as active until now; release
             // that extra refcount so session appears idle.
-            close();
+            closeInternal(false);
             return;
         }
 
@@ -1388,11 +1388,20 @@
 
     @Override
     public void close() {
+        closeInternal(true);
+    }
+
+    private void closeInternal(boolean checkCaller) {
+        int activeCount;
         synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
+            if (checkCaller) {
+                assertCallerIsOwnerOrRootLocked();
+            }
+
+            activeCount = mActiveCount.decrementAndGet();
         }
 
-        if (mActiveCount.decrementAndGet() == 0) {
+        if (activeCount == 0) {
             mCallback.onSessionActiveChanged(this, false);
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b68630e..e2e866e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -143,6 +143,7 @@
 import android.content.pm.IPackageInstallObserver2;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageManager;
+import android.content.pm.IPackageManagerNative;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstantAppInfo;
@@ -448,6 +449,7 @@
     static final int SCAN_FIRST_BOOT_OR_UPGRADE = 1<<16;
     static final int SCAN_AS_INSTANT_APP = 1<<17;
     static final int SCAN_AS_FULL_APP = 1<<18;
+    static final int SCAN_AS_VIRTUAL_PRELOAD = 1<<19;
     /** Should not be with the scan flags */
     static final int FLAGS_REMOVE_CHATTY = 1<<31;
 
@@ -2304,6 +2306,8 @@
                 factoryTest, onlyCore);
         m.enableSystemUserPackages();
         ServiceManager.addService("package", m);
+        final PackageManagerNative pmn = m.new PackageManagerNative();
+        ServiceManager.addService("package_native", pmn);
         return m;
     }
 
@@ -6409,7 +6413,18 @@
             return null;
         }
         synchronized (mPackages) {
-            return getNameForUidLocked(callingUid, uid);
+            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+            if (obj instanceof SharedUserSetting) {
+                final SharedUserSetting sus = (SharedUserSetting) obj;
+                return sus.name + ":" + sus.userId;
+            } else if (obj instanceof PackageSetting) {
+                final PackageSetting ps = (PackageSetting) obj;
+                if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+                    return null;
+                }
+                return ps.name;
+            }
+            return null;
         }
     }
 
@@ -6426,27 +6441,25 @@
         synchronized (mPackages) {
             for (int i = uids.length - 1; i >= 0; i--) {
                 final int uid = uids[i];
-                names[i] = getNameForUidLocked(callingUid, uid);
+                Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+                if (obj instanceof SharedUserSetting) {
+                    final SharedUserSetting sus = (SharedUserSetting) obj;
+                    names[i] = "shared:" + sus.name;
+                } else if (obj instanceof PackageSetting) {
+                    final PackageSetting ps = (PackageSetting) obj;
+                    if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+                        names[i] = null;
+                    } else {
+                        names[i] = ps.name;
+                    }
+                } else {
+                    names[i] = null;
+                }
             }
         }
         return names;
     }
 
-    private String getNameForUidLocked(int callingUid, int uid) {
-        Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
-        if (obj instanceof SharedUserSetting) {
-            final SharedUserSetting sus = (SharedUserSetting) obj;
-            return sus.name + ":" + sus.userId;
-        } else if (obj instanceof PackageSetting) {
-            final PackageSetting ps = (PackageSetting) obj;
-            if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
-                return null;
-            }
-            return ps.name;
-        }
-        return null;
-    }
-
     @Override
     public int getUidForSharedUser(String sharedUserName) {
         if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
@@ -9392,6 +9405,9 @@
         if (ps != null && ps.getInstantApp(userId)) {
             scanFlags |= SCAN_AS_INSTANT_APP;
         }
+        if (ps != null && ps.getVirtulalPreload(userId)) {
+            scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
+        }
 
         // Note that we invoke the following method only if we are about to unpack an application
         PackageParser.Package scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags
@@ -9875,17 +9891,19 @@
         Collection<PackageParser.Package> deps = findSharedNonSystemLibraries(p);
         final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
         if (!deps.isEmpty()) {
+            DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
+                    options.getCompilerFilter(), options.getSplitName(),
+                    options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
             for (PackageParser.Package depPackage : deps) {
                 // TODO: Analyze and investigate if we (should) profile libraries.
                 pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
                         getOrCreateCompilerPackageStats(depPackage),
-                        true /* isUsedByOtherApps */,
-                        options);
+                    mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
             }
         }
         return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets,
                 getOrCreateCompilerPackageStats(p),
-                mDexManager.isUsedByOtherApps(p.packageName), options);
+                mDexManager.getPackageUseInfoOrDefault(p.packageName), options);
     }
 
     /**
@@ -10011,7 +10029,7 @@
     public void shutdown() {
         mPackageUsage.writeNow(mPackages);
         mCompilerStats.writeNow();
-        mDexManager.savePackageDexUsageNow();
+        mDexManager.writePackageDexUsageNow();
     }
 
     @Override
@@ -10642,15 +10660,17 @@
                 final String parentPackageName = (pkg.parentPackage != null)
                         ? pkg.parentPackage.packageName : null;
                 final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+                final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
                 // REMOVE SharedUserSetting from method; update in a separate call
                 pkgSetting = Settings.createNewSetting(pkg.packageName, origPackage,
                         disabledPkgSetting, realName, suid, destCodeFile, destResourceFile,
                         pkg.applicationInfo.nativeLibraryRootDir, pkg.applicationInfo.primaryCpuAbi,
                         pkg.applicationInfo.secondaryCpuAbi, pkg.mVersionCode,
                         pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, user,
-                        true /*allowInstall*/, instantApp, parentPackageName,
-                        pkg.getChildPackageNames(), UserManagerService.getInstance(),
-                        usesStaticLibraries, pkg.usesStaticLibrariesVersions);
+                        true /*allowInstall*/, instantApp, virtualPreload,
+                        parentPackageName, pkg.getChildPackageNames(),
+                        UserManagerService.getInstance(), usesStaticLibraries,
+                        pkg.usesStaticLibrariesVersions);
                 // SIDE EFFECTS; updates system state; move elsewhere
                 if (origPackage != null) {
                     mSettings.addRenamedPackageLPw(pkg.packageName, origPackage.name);
@@ -18166,6 +18186,8 @@
         final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
         final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
         final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0);
+        final boolean virtualPreload =
+                ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
         boolean replace = false;
         int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
         if (args.move != null) {
@@ -18181,6 +18203,9 @@
         if (fullApp) {
             scanFlags |= SCAN_AS_FULL_APP;
         }
+        if (virtualPreload) {
+            scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
+        }
 
         // Result object to be returned
         res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
@@ -18560,7 +18585,7 @@
                 mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
                         null /* instructionSets */,
                         getOrCreateCompilerPackageStats(pkg),
-                        mDexManager.isUsedByOtherApps(pkg.packageName),
+                        mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
                         dexoptOptions);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
@@ -19971,6 +19996,7 @@
                     false /*hidden*/,
                     false /*suspended*/,
                     false /*instantApp*/,
+                    false /*virtualPreload*/,
                     null /*lastDisableAppCaller*/,
                     null /*enabledComponents*/,
                     null /*disabledComponents*/,
@@ -22858,7 +22884,8 @@
         for (PackageParser.Package pkg : packages) {
             ipw.println("[" + pkg.packageName + "]");
             ipw.increaseIndent();
-            mPackageDexOptimizer.dumpDexoptState(ipw, pkg);
+            mPackageDexOptimizer.dumpDexoptState(ipw, pkg,
+                    mDexManager.getPackageUseInfoOrDefault(pkg.packageName));
             ipw.decreaseIndent();
         }
     }
@@ -24789,6 +24816,20 @@
         }
     }
 
+    private class PackageManagerNative extends IPackageManagerNative.Stub {
+        @Override
+        public String[] getNamesForUids(int[] uids) throws RemoteException {
+            final String[] results = PackageManagerService.this.getNamesForUids(uids);
+            // massage results so they can be parsed by the native binder
+            for (int i = results.length - 1; i >= 0; --i) {
+                if (results[i] == null) {
+                    results[i] = "";
+                }
+            }
+            return results;
+        }
+    }
+
     private class PackageManagerInternalImpl extends PackageManagerInternal {
         @Override
         public void setLocationPackagesProvider(PackagesProvider provider) {
@@ -25383,8 +25424,8 @@
                 if (ps == null) {
                     continue;
                 }
-                PackageDexUsage.PackageUseInfo packageUseInfo = getDexManager().getPackageUseInfo(
-                        pkg.packageName);
+                PackageDexUsage.PackageUseInfo packageUseInfo =
+                      getDexManager().getPackageUseInfoOrDefault(pkg.packageName);
                 if (PackageManagerServiceUtils
                         .isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
                                 downgradeTimeThresholdMillis, packageUseInfo,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index a7031c9..25fef0a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -25,7 +25,6 @@
 import android.annotation.NonNull;
 import android.app.AppGlobals;
 import android.content.Intent;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.ResolveInfo;
 import android.os.Build;
@@ -137,9 +136,11 @@
                 sortTemp, packageManagerService);
 
         // Give priority to apps used by other apps.
+        DexManager dexManager = packageManagerService.getDexManager();
         applyPackageFilter((pkg) ->
-                packageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName), result,
-                remainingPkgs, sortTemp, packageManagerService);
+                dexManager.getPackageUseInfoOrDefault(pkg.packageName)
+                        .isAnyCodePathUsedByOtherApps(),
+                result, remainingPkgs, sortTemp, packageManagerService);
 
         // Filter out packages that aren't recently used, add all remaining apps.
         // TODO: add a property to control this?
@@ -209,12 +210,10 @@
 
         // If the app was active in background during the threshold period and was used
         // by other packages.
-        // If packageUseInfo is null, it can be said that the package was not used by other
-        // packages.
         boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
                 - latestPackageUseTimeInMillis)
                 < thresholdTimeinMillis)
-                && (packageUseInfo != null && packageUseInfo.isUsedByOtherApps());
+                && packageUseInfo.isAnyCodePathUsedByOtherApps();
 
         return !isActiveInBackgroundAndUsedByOtherPackages;
     }
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index f685127..d3ca1fd 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -420,11 +420,19 @@
         modifyUserState(userId).instantApp = instantApp;
     }
 
+    boolean getVirtulalPreload(int userId) {
+        return readUserState(userId).virtualPreload;
+    }
+
+    void setVirtualPreload(boolean virtualPreload, int userId) {
+        modifyUserState(userId).virtualPreload = virtualPreload;
+    }
+
     void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
             boolean notLaunched, boolean hidden, boolean suspended, boolean instantApp,
-            String lastDisableAppCaller, ArraySet<String> enabledComponents,
-            ArraySet<String> disabledComponents, int domainVerifState,
-            int linkGeneration, int installReason) {
+            boolean virtualPreload, String lastDisableAppCaller,
+            ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
+            int domainVerifState, int linkGeneration, int installReason) {
         PackageUserState state = modifyUserState(userId);
         state.ceDataInode = ceDataInode;
         state.enabled = enabled;
@@ -440,6 +448,7 @@
         state.appLinkGeneration = linkGeneration;
         state.installReason = installReason;
         state.instantApp = instantApp;
+        state.virtualPreload = virtualPreload;
     }
 
     ArraySet<String> getEnabledComponents(int userId) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 45d0c58..d99408f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -226,6 +226,7 @@
     private static final String ATTR_APP_LINK_GENERATION = "app-link-generation";
     private static final String ATTR_INSTALL_REASON = "install-reason";
     private static final String ATTR_INSTANT_APP = "instant-app";
+    private static final String ATTR_VIRTUAL_PRELOAD = "virtual-preload";
 
     private static final String ATTR_PACKAGE_NAME = "packageName";
     private static final String ATTR_FINGERPRINT = "fingerprint";
@@ -697,8 +698,9 @@
             PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser,
             File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi,
             String secondaryCpuAbi, int versionCode, int pkgFlags, int pkgPrivateFlags,
-            UserHandle installUser, boolean allowInstall, boolean instantApp, String parentPkgName,
-            List<String> childPkgNames, UserManagerService userManager,
+            UserHandle installUser, boolean allowInstall, boolean instantApp,
+            boolean virtualPreload, String parentPkgName, List<String> childPkgNames,
+            UserManagerService userManager,
             String[] usesStaticLibraries, int[] usesStaticLibrariesVersions) {
         final PackageSetting pkgSetting;
         if (originalPkg != null) {
@@ -760,6 +762,7 @@
                                 false /*hidden*/,
                                 false /*suspended*/,
                                 instantApp,
+                                virtualPreload,
                                 null /*lastDisableAppCaller*/,
                                 null /*enabledComponents*/,
                                 null /*disabledComponents*/,
@@ -1693,6 +1696,7 @@
                                 false /*hidden*/,
                                 false /*suspended*/,
                                 false /*instantApp*/,
+                                false /*virtualPreload*/,
                                 null /*lastDisableAppCaller*/,
                                 null /*enabledComponents*/,
                                 null /*disabledComponents*/,
@@ -1766,6 +1770,8 @@
                             ATTR_BLOCK_UNINSTALL, false);
                     final boolean instantApp = XmlUtils.readBooleanAttribute(parser,
                             ATTR_INSTANT_APP, false);
+                    final boolean virtualPreload = XmlUtils.readBooleanAttribute(parser,
+                            ATTR_VIRTUAL_PRELOAD, false);
                     final int enabled = XmlUtils.readIntAttribute(parser, ATTR_ENABLED,
                             COMPONENT_ENABLED_STATE_DEFAULT);
                     final String enabledCaller = parser.getAttributeValue(null,
@@ -1805,8 +1811,8 @@
                         setBlockUninstallLPw(userId, name, true);
                     }
                     ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
-                            hidden, suspended, instantApp, enabledCaller, enabledComponents,
-                            disabledComponents, verifState, linkGeneration,
+                            hidden, suspended, instantApp, virtualPreload, enabledCaller,
+                            enabledComponents, disabledComponents, verifState, linkGeneration,
                             installReason);
                 } else if (tagName.equals("preferred-activities")) {
                     readPreferredActivitiesLPw(parser, userId);
@@ -2117,6 +2123,9 @@
                 if (ustate.instantApp) {
                     serializer.attribute(null, ATTR_INSTANT_APP, "true");
                 }
+                if (ustate.virtualPreload) {
+                    serializer.attribute(null, ATTR_VIRTUAL_PRELOAD, "true");
+                }
                 if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
                     serializer.attribute(null, ATTR_ENABLED,
                             Integer.toString(ustate.enabled));
@@ -4603,6 +4612,7 @@
                 pw.print(ps.getStopped(user.id) ? "S" : "s");
                 pw.print(ps.getNotLaunched(user.id) ? "l" : "L");
                 pw.print(ps.getInstantApp(user.id) ? "IA" : "ia");
+                pw.print(ps.getVirtulalPreload(user.id) ? "VPI" : "vpi");
                 pw.print(",");
                 pw.print(ps.getEnabled(user.id));
                 String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
@@ -4858,6 +4868,8 @@
             pw.print(ps.getEnabled(user.id));
             pw.print(" instant=");
             pw.println(ps.getInstantApp(user.id));
+            pw.print(" virtual=");
+            pw.println(ps.getVirtulalPreload(user.id));
 
             String[] overlayPaths = ps.getOverlayPaths(user.id);
             if (overlayPaths != null && overlayPaths.length > 0) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 947e01c4..6274754 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -36,6 +36,8 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -81,6 +83,19 @@
     private static int DEX_SEARCH_FOUND_SPLIT = 2;  // dex file is a split apk
     private static int DEX_SEARCH_FOUND_SECONDARY = 3;  // dex file is a secondary dex
 
+    /**
+     * We do not record packages that have no secondary dex files or that are not used by other
+     * apps. This is an optimization to reduce the amount of data that needs to be written to
+     * disk (apps will not usually be shared so this trims quite a bit the number we record).
+     *
+     * To make this behaviour transparent to the callers which need use information on packages,
+     * DexManager will return this DEFAULT instance from
+     * {@link DexManager#getPackageUseInfoOrDefault}. It has no data about secondary dex files and
+     * is marked as not being used by other apps. This reflects the intended behaviour when we don't
+     * find the package in the underlying data file.
+     */
+    private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();
+
     public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
             Installer installer, Object installLock) {
       mPackageCodeLocationsCache = new HashMap<>();
@@ -297,6 +312,8 @@
 
     private void loadInternal(Map<Integer, List<PackageInfo>> existingPackages) {
         Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
+        Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+
         // Cache the code locations for the installed packages. This allows for
         // faster lookups (no locks) when finding what package owns the dex file.
         for (Map.Entry<Integer, List<PackageInfo>> entry : existingPackages.entrySet()) {
@@ -306,25 +323,53 @@
                 // Cache the code locations.
                 cachePackageInfo(pi, userId);
 
-                // Cache a map from package name to the set of user ids who installed the package.
+                // Cache two maps:
+                //   - from package name to the set of user ids who installed the package.
+                //   - from package name to the set of code paths.
                 // We will use it to sync the data and remove obsolete entries from
                 // mPackageDexUsage.
                 Set<Integer> users = putIfAbsent(
                         packageToUsersMap, pi.packageName, new HashSet<>());
                 users.add(userId);
+
+                Set<String> codePaths = putIfAbsent(
+                    packageToCodePaths, pi.packageName, new HashSet<>());
+                codePaths.add(pi.applicationInfo.sourceDir);
+                if (pi.applicationInfo.splitSourceDirs != null) {
+                    Collections.addAll(codePaths, pi.applicationInfo.splitSourceDirs);
+                }
             }
         }
 
         mPackageDexUsage.read();
-        mPackageDexUsage.syncData(packageToUsersMap);
+        mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
     }
 
     /**
      * Get the package dex usage for the given package name.
-     * @return the package data or null if there is no data available for this package.
+     * If there is no usage info the method will return a default {@code PackageUseInfo} with
+     * no data about secondary dex files and marked as not being used by other apps.
+     *
+     * Note that no use info means the package was not used or it was used but not by other apps.
+     * Also, note that right now we might prune packages which are not used by other apps.
+     * TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
+     * to access the package use.
      */
-    public PackageUseInfo getPackageUseInfo(String packageName) {
-        return mPackageDexUsage.getPackageUseInfo(packageName);
+    public PackageUseInfo getPackageUseInfoOrDefault(String packageName) {
+        PackageUseInfo useInfo = mPackageDexUsage.getPackageUseInfo(packageName);
+        return useInfo == null ? DEFAULT_USE_INFO : useInfo;
+    }
+
+    /**
+     * Return whether or not the manager has usage information on the give package.
+     *
+     * Note that no use info means the package was not used or it was used but not by other apps.
+     * Also, note that right now we might prune packages which are not used by other apps.
+     * TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
+     * to access the package use.
+     */
+    /*package*/ boolean hasInfoOnPackage(String packageName) {
+        return mPackageDexUsage.getPackageUseInfo(packageName) != null;
     }
 
     /**
@@ -343,7 +388,7 @@
                 ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
                 : mPackageDexOptimizer;
         String packageName = options.getPackageName();
-        PackageUseInfo useInfo = getPackageUseInfo(packageName);
+        PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
         if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
             if (DEBUG) {
                 Slog.d(TAG, "No secondary dex use for package:" + packageName);
@@ -387,7 +432,7 @@
      * deleted, update the internal records and delete any generated oat files.
      */
     public void reconcileSecondaryDexFiles(String packageName) {
-        PackageUseInfo useInfo = getPackageUseInfo(packageName);
+        PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
         if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
             if (DEBUG) {
                 Slog.d(TAG, "No secondary dex use for package:" + packageName);
@@ -519,23 +564,6 @@
     }
 
     /**
-     * Return true if the profiling data collected for the given app indicate
-     * that the apps's APK has been loaded by another app.
-     * Note that this returns false for all apps without any collected profiling data.
-    */
-    public boolean isUsedByOtherApps(String packageName) {
-        PackageUseInfo useInfo = getPackageUseInfo(packageName);
-        if (useInfo == null) {
-            // No use info, means the package was not used or it was used but not by other apps.
-            // Note that right now we might prune packages which are not used by other apps.
-            // TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
-            // to access the package use.
-            return false;
-        }
-        return useInfo.isUsedByOtherApps();
-    }
-
-    /**
      * Retrieves the package which owns the given dexPath.
      */
     private DexSearchResult getDexPackage(
@@ -593,9 +621,9 @@
     }
 
     /**
-     * Saves the in-memory package dex usage to disk right away.
+     * Writes the in-memory package dex usage to disk right away.
      */
-    public void savePackageDexUsageNow() {
+    public void writePackageDexUsageNow() {
         mPackageDexUsage.writeNow();
     }
 
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index f57cf5e..4fa47b5 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -50,6 +50,12 @@
     // save disk space.
     public static final int DEXOPT_DOWNGRADE = 1 << 5;
 
+    // When set, dexopt will compile the dex file as a shared library even if it is not actually
+    // used by other apps. This is used to force the compilation or shared libraries declared
+    // with in the manifest with ''uses-library' before we have a chance to detect they are
+    // actually shared at runtime.
+    public static final int DEXOPT_AS_SHARED_LIBRARY = 1 << 6;
+
     // The name of package to optimize.
     private final String mPackageName;
 
@@ -79,7 +85,8 @@
                 DEXOPT_BOOT_COMPLETE |
                 DEXOPT_ONLY_SECONDARY_DEX |
                 DEXOPT_ONLY_SHARED_DEX |
-                DEXOPT_DOWNGRADE;
+                DEXOPT_DOWNGRADE |
+                DEXOPT_AS_SHARED_LIBRARY;
         if ((flags & (~validityMask)) != 0) {
             throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
         }
@@ -122,7 +129,15 @@
         return (mFlags & DEXOPT_DOWNGRADE) != 0;
     }
 
+    public boolean isDexoptAsSharedLibrary() {
+        return (mFlags & DEXOPT_AS_SHARED_LIBRARY) != 0;
+    }
+
     public String getSplitName() {
         return mSplitName;
     }
+
+    public int getFlags() {
+        return mFlags;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 6ee26d3..a4a0a54 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -35,6 +35,7 @@
 import java.io.StringWriter;
 import java.io.Writer;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.HashMap;
@@ -53,17 +54,21 @@
 public class PackageDexUsage extends AbstractStatsBase<Void> {
     private final static String TAG = "PackageDexUsage";
 
-    // The last version update: add class loader contexts for secondary dex files.
-    private final static int PACKAGE_DEX_USAGE_VERSION = 3;
     // We support previous version to ensure that the usage list remains valid cross OTAs.
     private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1 = 1;
-    // Version 2 added the list of packages that load the dex files.
+    // Version 2 added:
+    //  - the list of packages that load the dex files
+    //  - class loader contexts for secondary dex files
+    //  - usage for all code paths (including splits)
     private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2 = 2;
 
+    private final static int PACKAGE_DEX_USAGE_VERSION = PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;
+
     private final static String PACKAGE_DEX_USAGE_VERSION_HEADER =
             "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__";
 
     private final static String SPLIT_CHAR = ",";
+    private final static String CODE_PATH_LINE_CHAR = "+";
     private final static String DEX_LINE_CHAR = "#";
     private final static String LOADING_PACKAGE_CHAR = "@";
 
@@ -130,9 +135,8 @@
                     // If we have a primary or a split apk, set isUsedByOtherApps.
                     // We do not need to record the loaderIsa or the owner because we compile
                     // primaries for all users and all ISAs.
-                    packageUseInfo.mIsUsedByOtherApps = isUsedByOtherApps;
-                    maybeAddLoadingPackage(owningPackageName, loadingPackageName,
-                            packageUseInfo.mLoadingPackages);
+                    packageUseInfo.mergeCodePathUsedByOtherApps(dexPath, isUsedByOtherApps,
+                            owningPackageName, loadingPackageName);
                 } else {
                     // For secondary dex files record the loaderISA and the owner. We'll need
                     // to know under which user to compile and for what ISA.
@@ -149,9 +153,8 @@
                 if (primaryOrSplit) {
                     // We have a possible update on the primary apk usage. Merge
                     // isUsedByOtherApps information and return if there was an update.
-                    boolean updateLoadingPackages = maybeAddLoadingPackage(owningPackageName,
-                            loadingPackageName, packageUseInfo.mLoadingPackages);
-                    return packageUseInfo.merge(isUsedByOtherApps) || updateLoadingPackages;
+                    return packageUseInfo.mergeCodePathUsedByOtherApps(
+                            dexPath, isUsedByOtherApps, owningPackageName, loadingPackageName);
                 } else {
                     DexUseInfo newData = new DexUseInfo(
                             isUsedByOtherApps, ownerUserId, classLoaderContext, loaderIsa);
@@ -230,22 +233,18 @@
      *
      * file_magic_version
      * package_name_1
+     * +code_path1
      * @ loading_package_1_1, loading_package_1_2...
+     * +code_path2
+     * @ loading_package_2_1, loading_package_2_2...
      * #dex_file_path_1_1
-     * @ loading_package_1_1_1, loading_package_1_1_2...
      * user_1_1, used_by_other_app_1_1, user_isa_1_1_1, user_isa_1_1_2
+     * @ loading_package_1_1_1, loading_package_1_1_2...
+     * class_loader_context_1_1
      * #dex_file_path_1_2
-     * @ loading_package_1_2_1, loading_package_1_2_2...
      * user_1_2, used_by_other_app_1_2, user_isa_1_2_1, user_isa_1_2_2
-     * ...
-     * package_name_2
-     * @ loading_package_2_1, loading_package_2_1_2...
-     * #dex_file_path_2_1
-     * @ loading_package_2_1_1, loading_package_2_1_2...
-     * user_2_1, used_by_other_app_2_1, user_isa_2_1_1, user_isa_2_1_2
-     * #dex_file_path_2_2,
-     * @ loading_package_2_2_1, loading_package_2_2_2...
-     * user_2_2, used_by_other_app_2_2, user_isa_2_2_1, user_isa_2_2_2
+     * @ loading_package_1_2_1, loading_package_1_2_2...
+     * class_loader_context_1_2
      * ...
     */
     /* package */ void write(Writer out) {
@@ -262,27 +261,31 @@
             // Write the package line.
             String packageName = pEntry.getKey();
             PackageUseInfo packageUseInfo = pEntry.getValue();
+            fpw.println(packageName);
 
-            fpw.println(String.join(SPLIT_CHAR, packageName,
-                    writeBoolean(packageUseInfo.mIsUsedByOtherApps)));
-            fpw.println(LOADING_PACKAGE_CHAR +
-                    String.join(SPLIT_CHAR, packageUseInfo.mLoadingPackages));
+            // Write the code paths used by other apps.
+            for (Map.Entry<String, Set<String>> codeEntry :
+                    packageUseInfo.mCodePathsUsedByOtherApps.entrySet()) {
+                String codePath = codeEntry.getKey();
+                Set<String> loadingPackages = codeEntry.getValue();
+                fpw.println(CODE_PATH_LINE_CHAR + codePath);
+                fpw.println(LOADING_PACKAGE_CHAR + String.join(SPLIT_CHAR, loadingPackages));
+            }
 
             // Write dex file lines.
             for (Map.Entry<String, DexUseInfo> dEntry : packageUseInfo.mDexUseInfoMap.entrySet()) {
                 String dexPath = dEntry.getKey();
                 DexUseInfo dexUseInfo = dEntry.getValue();
                 fpw.println(DEX_LINE_CHAR + dexPath);
-                fpw.println(LOADING_PACKAGE_CHAR +
-                        String.join(SPLIT_CHAR, dexUseInfo.mLoadingPackages));
-                fpw.println(dexUseInfo.getClassLoaderContext());
-
                 fpw.print(String.join(SPLIT_CHAR, Integer.toString(dexUseInfo.mOwnerUserId),
-                        writeBoolean(dexUseInfo.mIsUsedByOtherApps)));
+                    writeBoolean(dexUseInfo.mIsUsedByOtherApps)));
                 for (String isa : dexUseInfo.mLoaderIsas) {
                     fpw.print(SPLIT_CHAR + isa);
                 }
                 fpw.println();
+                fpw.println(LOADING_PACKAGE_CHAR
+                        + String.join(SPLIT_CHAR, dexUseInfo.mLoadingPackages));
+                fpw.println(dexUseInfo.getClassLoaderContext());
             }
         }
         fpw.flush();
@@ -324,7 +327,7 @@
             }
         }
 
-        String s;
+        String line;
         String currentPackage = null;
         PackageUseInfo currentPackageData = null;
 
@@ -332,8 +335,8 @@
         for (String abi : Build.SUPPORTED_ABIS) {
             supportedIsas.add(VMRuntime.getInstructionSet(abi));
         }
-        while ((s = in.readLine()) != null) {
-            if (s.startsWith(DEX_LINE_CHAR)) {
+        while ((line = in.readLine()) != null) {
+            if (line.startsWith(DEX_LINE_CHAR)) {
                 // This is the start of the the dex lines.
                 // We expect 4 lines for each dex entry:
                 // #dexPaths
@@ -345,25 +348,23 @@
                         "Malformed PackageDexUsage file. Expected package line before dex line.");
                 }
 
-                // First line is the dex path.
-                String dexPath = s.substring(DEX_LINE_CHAR.length());
+                // Line 1 is the dex path.
+                String dexPath = line.substring(DEX_LINE_CHAR.length());
 
-                // In version 2 the second line contains the list of packages that loaded the file.
-                List<String> loadingPackages = maybeReadLoadingPackages(in, version);
-                // In version 3 the third line contains the class loader context.
-                String classLoaderContext = maybeReadClassLoaderContext(in, version);
-
-                // Next line is the dex data.
-                s = in.readLine();
-                if (s == null) {
+                // Line 2 is the dex data: (userId, isUsedByOtherApps, isa).
+                line = in.readLine();
+                if (line == null) {
                     throw new IllegalStateException("Could not find dexUseInfo line");
                 }
-
-                // We expect at least 3 elements (isUsedByOtherApps, userId, isa).
-                String[] elems = s.split(SPLIT_CHAR);
+                String[] elems = line.split(SPLIT_CHAR);
                 if (elems.length < 3) {
-                    throw new IllegalStateException("Invalid PackageDexUsage line: " + s);
+                    throw new IllegalStateException("Invalid PackageDexUsage line: " + line);
                 }
+
+                // In version 2 we added the loading packages and class loader context.
+                Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
+                String classLoaderContext = maybeReadClassLoaderContext(in, version);
+
                 int ownerUserId = Integer.parseInt(elems[0]);
                 boolean isUsedByOtherApps = readBoolean(elems[1]);
                 DexUseInfo dexUseInfo = new DexUseInfo(isUsedByOtherApps, ownerUserId,
@@ -386,17 +387,35 @@
                     continue;
                 }
                 currentPackageData.mDexUseInfoMap.put(dexPath, dexUseInfo);
+            } else if (line.startsWith(CODE_PATH_LINE_CHAR)) {
+                // This is a code path used by other apps line.
+                if (version < PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
+                    throw new IllegalArgumentException("Unexpected code path line when parsing " +
+                            "PackageDexUseData: " + line);
+                }
+
+                // Expects 2 lines:
+                //    +code_paths
+                //    @loading_packages
+                String codePath = line.substring(CODE_PATH_LINE_CHAR.length());
+                Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
+                currentPackageData.mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
             } else {
                 // This is a package line.
-                // We expect it to be: `packageName,isUsedByOtherApps`.
-                String[] elems = s.split(SPLIT_CHAR);
-                if (elems.length != 2) {
-                    throw new IllegalStateException("Invalid PackageDexUsage line: " + s);
+                if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
+                    currentPackage = line;
+                    currentPackageData = new PackageUseInfo();
+                } else {
+                    // Old version (<2)
+                    // We expect it to be: `packageName,isUsedByOtherApps`.
+                    String[] elems = line.split(SPLIT_CHAR);
+                    if (elems.length != 2) {
+                        throw new IllegalStateException("Invalid PackageDexUsage line: " + line);
+                    }
+                    currentPackage = elems[0];
+                    currentPackageData = new PackageUseInfo();
+                    currentPackageData.mUsedByOtherAppsBeforeUpgrade = readBoolean(elems[1]);
                 }
-                currentPackage = elems[0];
-                currentPackageData = new PackageUseInfo();
-                currentPackageData.mIsUsedByOtherApps = readBoolean(elems[1]);
-                currentPackageData.mLoadingPackages.addAll(maybeReadLoadingPackages(in, version));
                 data.put(currentPackage, currentPackageData);
             }
         }
@@ -413,7 +432,7 @@
      */
     private String maybeReadClassLoaderContext(BufferedReader in, int version) throws IOException {
         String context = null;
-        if (version == PACKAGE_DEX_USAGE_VERSION) {
+        if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
             context = in.readLine();
             if (context == null) {
                 throw new IllegalStateException("Could not find the classLoaderContext line.");
@@ -429,7 +448,7 @@
      * Reads the list of loading packages from the buffer {@code in} if
      * {@code version} is at least {PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2}.
      */
-    private List<String> maybeReadLoadingPackages(BufferedReader in, int version)
+    private Set<String> maybeReadLoadingPackages(BufferedReader in, int version)
             throws IOException {
         if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
             String line = in.readLine();
@@ -438,13 +457,15 @@
             }
             // We expect that most of the times the list of loading packages will be empty.
             if (line.length() == LOADING_PACKAGE_CHAR.length()) {
-                return Collections.emptyList();
+                return Collections.emptySet();
             } else {
-                return Arrays.asList(
+                Set<String> result = new HashSet<>();
+                Collections.addAll(result,
                         line.substring(LOADING_PACKAGE_CHAR.length()).split(SPLIT_CHAR));
+                return result;
             }
         } else {
-            return Collections.emptyList();
+            return Collections.emptySet();
         }
     }
 
@@ -458,14 +479,15 @@
     }
 
     private boolean isSupportedVersion(int version) {
-        return version == PACKAGE_DEX_USAGE_VERSION ||
-                version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1;
+        return version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1
+                || version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;
     }
 
     /**
      * Syncs the existing data with the set of available packages by removing obsolete entries.
      */
-    public void syncData(Map<String, Set<Integer>> packageToUsersMap) {
+    /*package*/ void syncData(Map<String, Set<Integer>> packageToUsersMap,
+            Map<String, Set<String>> packageToCodePaths) {
         synchronized (mPackageUseInfoMap) {
             Iterator<Map.Entry<String, PackageUseInfo>> pIt =
                     mPackageUseInfoMap.entrySet().iterator();
@@ -489,8 +511,26 @@
                             dIt.remove();
                         }
                     }
-                    if (!packageUseInfo.mIsUsedByOtherApps
-                            && packageUseInfo.mDexUseInfoMap.isEmpty()) {
+
+                    // Sync the code paths.
+                    Set<String> codePaths = packageToCodePaths.get(packageName);
+                    Iterator<Map.Entry<String, Set<String>>> codeIt =
+                        packageUseInfo.mCodePathsUsedByOtherApps.entrySet().iterator();
+                    while (codeIt.hasNext()) {
+                        if (!codePaths.contains(codeIt.next().getKey())) {
+                            codeIt.remove();
+                        }
+                    }
+
+                    // In case the package was marked as used by other apps in a previous version
+                    // propagate the flag to all the code paths.
+                    // See mUsedByOtherAppsBeforeUpgrade docs on why it is important to do it.
+                    if (packageUseInfo.mUsedByOtherAppsBeforeUpgrade) {
+                        for (String codePath : codePaths) {
+                            packageUseInfo.mergeCodePathUsedByOtherApps(codePath, true, null, null);
+                        }
+                    } else if (!packageUseInfo.isAnyCodePathUsedByOtherApps()
+                        && packageUseInfo.mDexUseInfoMap.isEmpty()) {
                         // The package is not used by other apps and we removed all its dex files
                         // records. Remove the entire package record as well.
                         pIt.remove();
@@ -504,14 +544,13 @@
      * Clears the {@code usesByOtherApps} marker for the package {@code packageName}.
      * @return true if the package usage info was updated.
      */
-    public boolean clearUsedByOtherApps(String packageName) {
+    /*package*/ boolean clearUsedByOtherApps(String packageName) {
         synchronized (mPackageUseInfoMap) {
             PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
-            if (packageUseInfo == null || !packageUseInfo.mIsUsedByOtherApps) {
+            if (packageUseInfo == null) {
                 return false;
             }
-            packageUseInfo.mIsUsedByOtherApps = false;
-            return true;
+            return packageUseInfo.clearCodePathUsedByOtherApps();
         }
     }
 
@@ -532,7 +571,7 @@
      * @return true if the record was found and actually deleted,
      *         false if the record doesn't exist
      */
-    public boolean removeUserPackage(String packageName, int userId) {
+    /*package*/ boolean removeUserPackage(String packageName, int userId) {
         synchronized (mPackageUseInfoMap) {
             PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
             if (packageUseInfo == null) {
@@ -550,7 +589,8 @@
             }
             // If no secondary dex info is left and the package is not used by other apps
             // remove the data since it is now useless.
-            if (packageUseInfo.mDexUseInfoMap.isEmpty() && !packageUseInfo.mIsUsedByOtherApps) {
+            if (packageUseInfo.mDexUseInfoMap.isEmpty()
+                    && !packageUseInfo.isAnyCodePathUsedByOtherApps()) {
                 mPackageUseInfoMap.remove(packageName);
                 updated = true;
             }
@@ -564,7 +604,7 @@
      * @return true if the record was found and actually deleted,
      *         false if the record doesn't exist
      */
-    public boolean removeDexFile(String packageName, String dexFile, int userId) {
+    /*package*/ boolean removeDexFile(String packageName, String dexFile, int userId) {
         synchronized (mPackageUseInfoMap) {
             PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
             if (packageUseInfo == null) {
@@ -586,7 +626,7 @@
         return false;
     }
 
-    public PackageUseInfo getPackageUseInfo(String packageName) {
+    /*package*/ PackageUseInfo getPackageUseInfo(String packageName) {
         synchronized (mPackageUseInfoMap) {
             PackageUseInfo useInfo = mPackageUseInfoMap.get(packageName);
             // The useInfo contains a map for secondary dex files which could be modified
@@ -601,7 +641,7 @@
     /**
      * Return all packages that contain records of secondary dex files.
      */
-    public Set<String> getAllPackagesWithSecondaryDexFiles() {
+    /*package*/ Set<String> getAllPackagesWithSecondaryDexFiles() {
         Set<String> packages = new HashSet<>();
         synchronized (mPackageUseInfoMap) {
             for (Map.Entry<String, PackageUseInfo> entry : mPackageUseInfoMap.entrySet()) {
@@ -639,15 +679,6 @@
         throw new IllegalArgumentException("Unknown bool encoding: " + bool);
     }
 
-    private boolean contains(int[] array, int elem) {
-        for (int i = 0; i < array.length; i++) {
-            if (elem == array[i]) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     public String dump() {
         StringWriter sw = new StringWriter();
         write(sw);
@@ -658,46 +689,94 @@
      * Stores data on how a package and its dex files are used.
      */
     public static class PackageUseInfo {
-        // This flag is for the primary and split apks. It is set to true whenever one of them
-        // is loaded by another app.
-        private boolean mIsUsedByOtherApps;
+        // The app's code paths that are used by other apps.
+        // The key is the code path and the value is the set of loading packages.
+        private final Map<String, Set<String>> mCodePathsUsedByOtherApps;
         // Map dex paths to their data (isUsedByOtherApps, owner id, loader isa).
         private final Map<String, DexUseInfo> mDexUseInfoMap;
-        // Packages who load this dex file.
-        private final Set<String> mLoadingPackages;
+
+        // Keeps track of whether or not this package was used by other apps before
+        // we upgraded to VERSION 4 which records the info for each code path separately.
+        // This is unwanted complexity but without it we risk to profile guide compile
+        // something that supposed to be shared. For example:
+        //   1) we determine that chrome is used by another app
+        //   2) we take an OTA which upgrades the way we keep track of usage data
+        //   3) chrome doesn't get used until the background job executes
+        //   4) as part of the backgound job we now think that chrome is not used by others
+        //      and we speed-profile.
+        //   5) as a result the next time someone uses chrome it will extract from apk since
+        //      the compiled code will be private.
+        private boolean mUsedByOtherAppsBeforeUpgrade;
 
         public PackageUseInfo() {
-            mIsUsedByOtherApps = false;
+            mCodePathsUsedByOtherApps = new HashMap<>();
             mDexUseInfoMap = new HashMap<>();
-            mLoadingPackages = new HashSet<>();
         }
 
         // Creates a deep copy of the `other`.
         public PackageUseInfo(PackageUseInfo other) {
-            mIsUsedByOtherApps = other.mIsUsedByOtherApps;
+            mCodePathsUsedByOtherApps = new HashMap<>();
+            for (Map.Entry<String, Set<String>> e : other.mCodePathsUsedByOtherApps.entrySet()) {
+                mCodePathsUsedByOtherApps.put(e.getKey(), new HashSet<>(e.getValue()));
+            }
+
             mDexUseInfoMap = new HashMap<>();
             for (Map.Entry<String, DexUseInfo> e : other.mDexUseInfoMap.entrySet()) {
                 mDexUseInfoMap.put(e.getKey(), new DexUseInfo(e.getValue()));
             }
-            mLoadingPackages = new HashSet<>(other.mLoadingPackages);
         }
 
-        private boolean merge(boolean isUsedByOtherApps) {
-            boolean oldIsUsedByOtherApps = mIsUsedByOtherApps;
-            mIsUsedByOtherApps = mIsUsedByOtherApps || isUsedByOtherApps;
-            return oldIsUsedByOtherApps != this.mIsUsedByOtherApps;
+        private boolean mergeCodePathUsedByOtherApps(String codePath, boolean isUsedByOtherApps,
+                String owningPackageName, String loadingPackage) {
+            if (!isUsedByOtherApps) {
+                // Nothing to update if the the code path is not used by other apps.
+                return false;
+            }
+
+            boolean newCodePath = false;
+            Set<String> loadingPackages = mCodePathsUsedByOtherApps.get(codePath);
+            if (loadingPackages == null) {
+                loadingPackages = new HashSet<>();
+                mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
+                newCodePath = true;
+            }
+            boolean newLoadingPackage = loadingPackage != null
+                    && !loadingPackage.equals(owningPackageName)
+                    && loadingPackages.add(loadingPackage);
+            return newCodePath || newLoadingPackage;
         }
 
-        public boolean isUsedByOtherApps() {
-            return mIsUsedByOtherApps;
+        public boolean isUsedByOtherApps(String codePath) {
+            return mCodePathsUsedByOtherApps.containsKey(codePath);
         }
 
         public Map<String, DexUseInfo> getDexUseInfoMap() {
             return mDexUseInfoMap;
         }
 
-        public Set<String> getLoadingPackages() {
-            return mLoadingPackages;
+        public Set<String> getLoadingPackages(String codePath) {
+            return mCodePathsUsedByOtherApps.getOrDefault(codePath, null);
+        }
+
+        public boolean isAnyCodePathUsedByOtherApps() {
+            return !mCodePathsUsedByOtherApps.isEmpty();
+        }
+
+        /**
+         * Clears the usedByOtherApps markers from all code paths.
+         * Returns whether or not there was an update.
+         */
+        /*package*/ boolean clearCodePathUsedByOtherApps() {
+            // Update mUsedByOtherAppsBeforeUpgrade as well to be consistent with
+            // the new data. This is not saved to disk so we don't need to return it.
+            mUsedByOtherAppsBeforeUpgrade = true;
+
+            if (mCodePathsUsedByOtherApps.isEmpty()) {
+                return false;
+            } else {
+                mCodePathsUsedByOtherApps.clear();
+                return true;
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 92f1c83..d0ca57a 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -58,7 +58,9 @@
 
 import com.android.internal.telephony.ITelephony;
 import com.android.server.RescueParty;
+import com.android.server.LocalServices;
 import com.android.server.pm.PackageManagerService;
+import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.File;
 import java.io.IOException;
@@ -289,6 +291,9 @@
                 pd.setMessage(context.getText(
                             com.android.internal.R.string.reboot_to_update_prepare));
             } else {
+                if (showSysuiReboot()) {
+                    return null;
+                }
                 pd.setIndeterminate(true);
                 pd.setMessage(context.getText(
                             com.android.internal.R.string.reboot_to_update_reboot));
@@ -308,37 +313,10 @@
                             com.android.internal.R.string.reboot_to_reset_message));
                 pd.setIndeterminate(true);
             }
-        } else if (mReason != null && mReason.equals(PowerManager.SHUTDOWN_USER_REQUESTED)) {
-            Dialog d = new Dialog(context);
-            d.setContentView(com.android.internal.R.layout.shutdown_dialog);
-            d.setCancelable(false);
-
-            int color;
-            try {
-                boolean onKeyguard = context.getSystemService(
-                        KeyguardManager.class).isKeyguardLocked();
-                WallpaperColors currentColors = context.getSystemService(WallpaperManager.class)
-                        .getWallpaperColors(onKeyguard ?
-                                WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM);
-                color = currentColors != null &&
-                        (currentColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT)
-                                != 0 ?
-                        Color.BLACK : Color.WHITE;
-            } catch (Exception e) {
-                color = Color.WHITE;
-            }
-
-            ProgressBar bar = d.findViewById(com.android.internal.R.id.progress);
-            bar.getIndeterminateDrawable().setTint(color);
-            ((TextView) d.findViewById(com.android.internal.R.id.text1)).setTextColor(color);
-            d.getWindow().getAttributes().width = ViewGroup.LayoutParams.MATCH_PARENT;
-            d.getWindow().getAttributes().height = ViewGroup.LayoutParams.MATCH_PARENT;
-            d.getWindow().setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
-            d.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-            d.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
-            d.show();
-            return null;
         } else {
+            if (showSysuiReboot()) {
+                return null;
+            }
             pd.setTitle(context.getText(com.android.internal.R.string.power_off));
             pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
             pd.setIndeterminate(true);
@@ -350,6 +328,23 @@
         return pd;
     }
 
+    private static boolean showSysuiReboot() {
+        Log.d(TAG, "Attempting to use SysUI shutdown UI");
+        try {
+            StatusBarManagerInternal service = LocalServices.getService(
+                    StatusBarManagerInternal.class);
+            if (service.showShutdownUi(mReboot, mReason)) {
+                // Sysui will handle shutdown UI.
+                Log.d(TAG, "SysUI handling shutdown UI");
+                return true;
+            }
+        } catch (Exception e) {
+            // If anything went wrong, ignore it and use fallback ui
+        }
+        Log.d(TAG, "SysUI is unavailable");
+        return false;
+    }
+
     private static void beginShutdownSequence(Context context) {
         synchronized (sIsStartedGuard) {
             if (sIsStarted) {
@@ -571,7 +566,7 @@
         Thread t = new Thread() {
             public void run() {
                 boolean nfcOff;
-                boolean bluetoothOff;
+                boolean bluetoothReadyForShutdown;
                 boolean radioOff;
 
                 final INfcAdapter nfc =
@@ -595,15 +590,15 @@
                 }
 
                 try {
-                    bluetoothOff = bluetooth == null ||
+                    bluetoothReadyForShutdown = bluetooth == null ||
                             bluetooth.getState() == BluetoothAdapter.STATE_OFF;
-                    if (!bluetoothOff) {
+                    if (!bluetoothReadyForShutdown) {
                         Log.w(TAG, "Disabling Bluetooth...");
                         bluetooth.disable(mContext.getPackageName(), false);  // disable but don't persist new state
                     }
                 } catch (RemoteException ex) {
                     Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
-                    bluetoothOff = true;
+                    bluetoothReadyForShutdown = true;
                 }
 
                 try {
@@ -628,14 +623,19 @@
                         sInstance.setRebootProgress(status, null);
                     }
 
-                    if (!bluetoothOff) {
+                    if (!bluetoothReadyForShutdown) {
                         try {
-                            bluetoothOff = bluetooth.getState() == BluetoothAdapter.STATE_OFF;
+                          // BLE only mode can happen when BT is turned off
+                          // We will continue shutting down in such case
+                          bluetoothReadyForShutdown =
+                                  bluetooth.getState() == BluetoothAdapter.STATE_OFF ||
+                                  bluetooth.getState() == BluetoothAdapter.STATE_BLE_TURNING_OFF ||
+                                  bluetooth.getState() == BluetoothAdapter.STATE_BLE_ON;
                         } catch (RemoteException ex) {
                             Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
-                            bluetoothOff = true;
+                            bluetoothReadyForShutdown = true;
                         }
-                        if (bluetoothOff) {
+                        if (bluetoothReadyForShutdown) {
                             Log.i(TAG, "Bluetooth turned off.");
                         }
                     }
@@ -662,7 +662,7 @@
                         }
                     }
 
-                    if (radioOff && bluetoothOff && nfcOff) {
+                    if (radioOff && bluetoothReadyForShutdown && nfcOff) {
                         Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
                         done[0] = true;
                         break;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 5e322da..866fdad 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -80,6 +80,8 @@
     void setGlobalActionsListener(GlobalActionsListener listener);
     void showGlobalActions();
 
+    boolean showShutdownUi(boolean isReboot, String requestString);
+
     public interface GlobalActionsListener {
         /**
          * Called when sysui starts and connects its status bar, or when the status bar binder
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index ae04b83..d31c230 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -36,6 +36,7 @@
 import android.util.ArrayMap;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
@@ -329,6 +330,20 @@
                 } catch (RemoteException ex) {}
             }
         }
+
+        @Override
+        public boolean showShutdownUi(boolean isReboot, String reason) {
+            if (!mContext.getResources().getBoolean(R.bool.config_showSysuiShutdown)) {
+                return false;
+            }
+            if (mBar != null) {
+                try {
+                    mBar.showShutdownUi(isReboot, reason);
+                    return true;
+                } catch (RemoteException ex) {}
+            }
+            return false;
+        }
     };
 
     // ================================================================================
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 40528d0..7bcad9f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -52,6 +52,7 @@
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
@@ -760,16 +761,21 @@
 
         if (mUpdateRotation) {
             if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
-
-            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-                final DisplayContent displayContent = mChildren.get(displayNdx);
-                final int displayId = displayContent.getDisplayId();
-                if (displayContent.updateRotationUnchecked(false /* inTransaction */)) {
-                    mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
-                } else if (displayId == DEFAULT_DISPLAY) {
-                    // TODO(multi-display): Track rotation updates for different displays separately
-                    mUpdateRotation = false;
-                }
+            // TODO(multi-display): Update rotation for different displays separately.
+            final int displayId = defaultDisplay.getDisplayId();
+            if (defaultDisplay.updateRotationUnchecked(false /* inTransaction */)) {
+                mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+            } else {
+                mUpdateRotation = false;
+            }
+            // Update rotation of VR virtual display separately. Currently this is the only kind of
+            // secondary display that can be rotated because of the single-display limitations in
+            // PhoneWindowManager.
+            final DisplayContent vrDisplay = mService.mVr2dDisplayId != INVALID_DISPLAY
+                    ? getDisplayContent(mService.mVr2dDisplayId) : null;
+            if (vrDisplay != null && vrDisplay.updateRotationUnchecked(false /* inTransaction */)) {
+                mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mService.mVr2dDisplayId)
+                        .sendToTarget();
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
index f5ef2e6..85ea3c0 100644
--- a/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
+++ b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
@@ -180,6 +180,11 @@
         // Compute new scaled width and height for background that will depend on current animation
         // progress. Those consist of current crop rect for the main surface + scaled areas outside
         // of letterboxed area.
+        // TODO: Because the progress is computed with low precision we're getting smaller values
+        // for background width/height then screen size at the end of the animation. Will round when
+        // the value is smaller then some empiric epsilon. However, this should be fixed by
+        // computing correct frames for letterboxed windows in WindowState.
+        d = d < 0.02f ? 0 : d;
         mWindowSurfaceController.getContainerRect(mTmpContainerRect);
         final int backgroundWidth =
                 (int) (crop.width() + (mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 83926af..fbc5c4a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3480,21 +3480,11 @@
             }
 
             if (!mBootAnimationStopped) {
-                // Do this one time.
                 Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
-                try {
-                    IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
-                    if (surfaceFlinger != null) {
-                        Slog.i(TAG_WM, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
-                        Parcel data = Parcel.obtain();
-                        data.writeInterfaceToken("android.ui.ISurfaceComposer");
-                        surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
-                                data, null, 0);
-                        data.recycle();
-                    }
-                } catch (RemoteException ex) {
-                    Slog.e(TAG_WM, "Boot completed: SurfaceFlinger is dead!");
-                }
+                // stop boot animation
+                // formerly we would just kill the process, but we now ask it to exit so it
+                // can choose where to stop the animation.
+                SystemProperties.set("service.bootanim.exit", "1");
                 mBootAnimationStopped = true;
             }
 
@@ -3503,6 +3493,20 @@
                 return;
             }
 
+            try {
+                IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
+                if (surfaceFlinger != null) {
+                    Slog.i(TAG_WM, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
+                    Parcel data = Parcel.obtain();
+                    data.writeInterfaceToken("android.ui.ISurfaceComposer");
+                    surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
+                            data, null, 0);
+                    data.recycle();
+                }
+            } catch (RemoteException ex) {
+                Slog.e(TAG_WM, "Boot completed: SurfaceFlinger is dead!");
+            }
+
             EventLog.writeEvent(EventLogTags.WM_BOOT_ANIMATION_DONE, SystemClock.uptimeMillis());
             Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
             mDisplayEnabled = true;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 25ba66e..3cd24b8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -382,6 +382,7 @@
                 null /*installUser*/,
                 false /*allowInstall*/,
                 false /*instantApp*/,
+                false /*virtualPreload*/,
                 null /*parentPkgName*/,
                 null /*childPkgNames*/,
                 UserManagerService.getInstance(),
@@ -423,6 +424,7 @@
                 UserHandle.SYSTEM /*installUser*/,
                 true /*allowInstall*/,
                 false /*instantApp*/,
+                false /*virtualPreload*/,
                 null /*parentPkgName*/,
                 null /*childPkgNames*/,
                 UserManagerService.getInstance(),
@@ -467,6 +469,7 @@
                 null /*installUser*/,
                 false /*allowInstall*/,
                 false /*instantApp*/,
+                false /*virtualPreload*/,
                 null /*parentPkgName*/,
                 null /*childPkgNames*/,
                 UserManagerService.getInstance(),
@@ -511,6 +514,7 @@
                 null /*installUser*/,
                 false /*allowInstall*/,
                 false /*instantApp*/,
+                false /*virtualPreload*/,
                 null /*parentPkgName*/,
                 null /*childPkgNames*/,
                 UserManagerService.getInstance(),
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index e2dfb29..4db9a30 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -106,7 +106,7 @@
         notifyDexLoad(mFooUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
 
         // Package is not used by others, so we should get nothing back.
-        assertNull(getPackageUseInfo(mFooUser0));
+        assertNoUseInfo(mFooUser0);
     }
 
     @Test
@@ -116,8 +116,7 @@
 
         // Bar is used by others now and should be in our records
         PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNotNull(pui);
-        assertTrue(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0, pui, true);
         assertTrue(pui.getDexUseInfoMap().isEmpty());
     }
 
@@ -128,8 +127,7 @@
         notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
 
         PackageUseInfo pui = getPackageUseInfo(mFooUser0);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mFooUser0, pui, false);
         assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
     }
@@ -141,8 +139,7 @@
         notifyDexLoad(mFooUser0, barSecondaries, mUser0);
 
         PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0, pui, false);
         assertEquals(barSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
     }
@@ -165,8 +162,7 @@
 
         // Check bar usage. Should be used by other app (for primary and barSecondaries).
         PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNotNull(pui);
-        assertTrue(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0, pui, true);
         assertEquals(barSecondaries.size() + barSecondariesForOwnUse.size(),
                 pui.getDexUseInfoMap().size());
 
@@ -176,8 +172,7 @@
 
         // Check foo usage. Should not be used by other app.
         pui = getPackageUseInfo(mFooUser0);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mFooUser0, pui, false);
         assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
     }
@@ -185,22 +180,22 @@
     @Test
     public void testPackageUseInfoNotFound() {
         // Assert we don't get back data we did not previously record.
-        assertNull(getPackageUseInfo(mFooUser0));
+        assertNoUseInfo(mFooUser0);
     }
 
     @Test
     public void testInvalidIsa() {
         // Notifying with an invalid ISA should be ignored.
         notifyDexLoad(mInvalidIsa, mInvalidIsa.getSecondaryDexPaths(), mUser0);
-        assertNull(getPackageUseInfo(mInvalidIsa));
+        assertNoUseInfo(mInvalidIsa);
     }
 
     @Test
-    public void testNotExistingPackate() {
+    public void testNotExistingPackage() {
         // Notifying about the load of a package which was previously not
         // register in DexManager#load should be ignored.
         notifyDexLoad(mDoesNotExist, mDoesNotExist.getBaseAndSplitDexPaths(), mUser0);
-        assertNull(getPackageUseInfo(mDoesNotExist));
+        assertNoUseInfo(mDoesNotExist);
     }
 
     @Test
@@ -208,7 +203,7 @@
         // Bar from User1 tries to load secondary dex files from User0 Bar.
         // Request should be ignored.
         notifyDexLoad(mBarUser1, mBarUser0.getSecondaryDexPaths(), mUser1);
-        assertNull(getPackageUseInfo(mBarUser1));
+        assertNoUseInfo(mBarUser1);
     }
 
     @Test
@@ -217,7 +212,7 @@
         // Note that the PackageManagerService already filters this out but we
         // still check that nothing goes unexpected in DexManager.
         notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser1);
-        assertNull(getPackageUseInfo(mBarUser1));
+        assertNoUseInfo(mBarUser1);
     }
 
     @Test
@@ -229,7 +224,7 @@
         // Before we notify about the installation of the newPackage if mFoo
         // is trying to load something from it we should not find it.
         notifyDexLoad(mFooUser0, newSecondaries, mUser0);
-        assertNull(getPackageUseInfo(newPackage));
+        assertNoUseInfo(newPackage);
 
         // Notify about newPackage install and let mFoo load its dexes.
         mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
@@ -237,8 +232,7 @@
 
         // We should get back the right info.
         PackageUseInfo pui = getPackageUseInfo(newPackage);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(newPackage, pui, false);
         assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
     }
@@ -254,8 +248,7 @@
         notifyDexLoad(newPackage, newSecondaries, mUser0);
 
         PackageUseInfo pui = getPackageUseInfo(newPackage);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(newPackage, pui, false);
         assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
     }
@@ -267,8 +260,7 @@
 
         // Bar is used by others now and should be in our records.
         PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNotNull(pui);
-        assertTrue(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0, pui, true);
         assertTrue(pui.getDexUseInfoMap().isEmpty());
 
         // Notify that bar is updated.
@@ -278,8 +270,7 @@
 
         // The usedByOtherApps flag should be clear now.
         pui = getPackageUseInfo(mBarUser0);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0, pui, false);
     }
 
     @Test
@@ -291,8 +282,7 @@
 
         // We shouldn't find yet the new split as we didn't notify the package update.
         notifyDexLoad(mFooUser0, newSplits, mUser0);
-        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNull(pui);
+        assertNoUseInfo(mBarUser0);
 
         // Notify that bar is updated. splitSourceDirs will contain the updated path.
         mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
@@ -301,9 +291,9 @@
 
         // Now, when the split is loaded we will find it and we should mark Bar as usedByOthers.
         notifyDexLoad(mFooUser0, newSplits, mUser0);
-        pui = getPackageUseInfo(mBarUser0);
+        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
         assertNotNull(pui);
-        assertTrue(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(newSplits, pui, true);
     }
 
     @Test
@@ -335,8 +325,7 @@
         // Foo should still be around since it's used by other apps but with no
         // secondary dex info.
         PackageUseInfo pui = getPackageUseInfo(mFooUser0);
-        assertNotNull(pui);
-        assertTrue(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mFooUser0, pui, true);
         assertTrue(pui.getDexUseInfoMap().isEmpty());
     }
 
@@ -350,8 +339,7 @@
 
         // Foo should not be around since all its secondary dex info were deleted
         // and it is not used by other apps.
-        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
-        assertNull(pui);
+        assertNoUseInfo(mFooUser0);
     }
 
     @Test
@@ -363,8 +351,7 @@
         mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), UserHandle.USER_ALL);
 
         // Bar should not be around since it was removed for all users.
-        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNull(pui);
+        assertNoUseInfo(mBarUser0);
     }
 
     @Test
@@ -373,7 +360,7 @@
         // Load a dex file from framework.
         notifyDexLoad(mFooUser0, Arrays.asList(frameworkDex), mUser0);
         // The dex file should not be recognized as a package.
-        assertNull(mDexManager.getPackageUseInfo(frameworkDex));
+        assertFalse(mDexManager.hasInfoOnPackage(frameworkDex));
     }
 
     @Test
@@ -383,8 +370,7 @@
         notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
 
         PackageUseInfo pui = getPackageUseInfo(mFooUser0);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mFooUser0, pui, false);
         assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
     }
@@ -395,8 +381,7 @@
         notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
 
         PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
         assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
         // We expect that all the contexts are unsupported.
         String[] expectedContexts =
@@ -413,8 +398,7 @@
 
         notifyDexLoad(mBarUser0, secondaries, mUser0);
         PackageUseInfo pui = getPackageUseInfo(mBarUser0);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0, pui, false);
         assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
         assertSecondaryUse(mFooUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
 
@@ -422,8 +406,7 @@
         notifyDexLoad(mBarUser0DelegateLastClassLoader, secondaries, mUser0);
 
         pui = getPackageUseInfo(mBarUser0);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0, pui, false);
         assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
         // We expect that all the contexts to be changed to variable now.
         String[] expectedContexts =
@@ -439,8 +422,7 @@
         notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
 
         PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
-        assertNotNull(pui);
-        assertFalse(pui.isUsedByOtherApps());
+        assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
         assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
         // We expect that all the contexts are unsupported.
         String[] expectedContexts =
@@ -484,6 +466,17 @@
                 expectedContexts);
     }
 
+    private void assertIsUsedByOtherApps(TestData testData, PackageUseInfo pui,
+            boolean isUsedByOtherApps) {
+        assertIsUsedByOtherApps(testData.getBaseAndSplitDexPaths(), pui, isUsedByOtherApps);
+    }
+
+    private void assertIsUsedByOtherApps(List<String> codePaths, PackageUseInfo pui,
+            boolean isUsedByOtherApps) {
+        for (String codePath : codePaths) {
+            assertEquals(codePath, isUsedByOtherApps, pui.isUsedByOtherApps(codePath));
+        }
+    }
     private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId) {
         // By default, assume a single class loader in the chain.
         // This makes writing tests much easier.
@@ -499,7 +492,12 @@
     }
 
     private PackageUseInfo getPackageUseInfo(TestData testData) {
-        return mDexManager.getPackageUseInfo(testData.mPackageInfo.packageName);
+        assertTrue(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
+        return mDexManager.getPackageUseInfoOrDefault(testData.mPackageInfo.packageName);
+    }
+
+    private void assertNoUseInfo(TestData testData) {
+        assertFalse(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
     }
 
     private static PackageInfo getMockPackageInfo(String packageName, int userId) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index 1eb5552..b64716c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -62,7 +62,8 @@
                 DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
                 DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
                 DexoptOptions.DEXOPT_ONLY_SHARED_DEX |
-                DexoptOptions.DEXOPT_DOWNGRADE;
+                DexoptOptions.DEXOPT_DOWNGRADE  |
+                DexoptOptions.DEXOPT_AS_SHARED_LIBRARY;
 
         DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags);
         assertEquals(mPackageName, opt.getPackageName());
@@ -74,6 +75,7 @@
         assertTrue(opt.isDexoptOnlySharedDex());
         assertTrue(opt.isDowngrade());
         assertTrue(opt.isForce());
+        assertTrue(opt.isDexoptAsSharedLibrary());
     }
 
     @Test
@@ -89,7 +91,7 @@
                 PackageManagerService.REASON_INSTALL,
                 PackageManagerService.REASON_BACKGROUND_DEXOPT,
                 PackageManagerService.REASON_AB_OTA,
-                PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE};
+                PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE,};
 
         for (int reason : reasons) {
             DexoptOptions opt = new DexoptOptions(mPackageName, reason, flags);
@@ -102,6 +104,7 @@
             assertFalse(opt.isDexoptOnlySharedDex());
             assertFalse(opt.isDowngrade());
             assertTrue(opt.isForce());
+            assertFalse(opt.isDexoptAsSharedLibrary());
         }
     }
 
@@ -119,6 +122,7 @@
         assertFalse(opt.isDexoptOnlySharedDex());
         assertFalse(opt.isDowngrade());
         assertTrue(opt.isForce());
+        assertFalse(opt.isDexoptAsSharedLibrary());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 3fc12b4..69a148d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -21,6 +21,7 @@
 import android.support.test.runner.AndroidJUnit4;
 import dalvik.system.VMRuntime;
 
+import java.util.Collections;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -249,7 +250,10 @@
         Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
         packageToUsersMap.put(mBarSecondary2User1.mPackageName,
                 new HashSet<>(Arrays.asList(mBarSecondary2User1.mOwnerUserId)));
-        mPackageDexUsage.syncData(packageToUsersMap);
+        Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+        packageToCodePaths.put(mBarBaseUser0.mPackageName,
+                new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
+        mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
 
         // Assert that only user 1 files are there.
         assertPackageDexUsage(mBarBaseUser0, mBarSecondary2User1);
@@ -341,8 +345,8 @@
         Set<String> usersExtra = new HashSet<>(Arrays.asList(
                 new String[] {"another.package.2", "another.package.3"}));
 
-        assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, users));
-        assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, usersExtra));
+        assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users));
+        assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra));
 
         assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users));
         assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra));
@@ -351,7 +355,7 @@
         // Verify that the users were recorded.
         Set<String> userAll = new HashSet<>(users);
         userAll.addAll(usersExtra);
-        assertPackageDexUsage(packageDexUsageRecordUsers, userAll, mFooBaseUser0,
+        assertPackageDexUsage(packageDexUsageRecordUsers, userAll, mFooSplit2UsedByOtherApps0,
                 mFooSecondary1User0);
     }
 
@@ -359,19 +363,19 @@
     public void testRecordDexFileUsersNotTheOwningPackage() {
         PackageDexUsage packageDexUsageRecordUsers = new PackageDexUsage();
         Set<String> users = new HashSet<>(Arrays.asList(
-                new String[] {mFooBaseUser0.mPackageName}));
+                new String[] {mFooSplit2UsedByOtherApps0.mPackageName}));
         Set<String> usersExtra = new HashSet<>(Arrays.asList(
                 new String[] {"another.package.2", "another.package.3"}));
 
-        assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, users));
-        assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, usersExtra));
+        assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users));
+        assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra));
 
         assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users));
         assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra));
 
         packageDexUsageRecordUsers = writeAndReadBack(packageDexUsageRecordUsers);
         // Verify that only the non owning packages were recorded.
-        assertPackageDexUsage(packageDexUsageRecordUsers, usersExtra, mFooBaseUser0,
+        assertPackageDexUsage(packageDexUsageRecordUsers, usersExtra, mFooSplit2UsedByOtherApps0,
                 mFooSecondary1User0);
     }
 
@@ -428,7 +432,6 @@
         assertPackageDexUsage(null, mFooSecondary1User0);
     }
 
-
     @Test
     public void testDexUsageClassLoaderContext() {
         final boolean isUsedByOtherApps = false;
@@ -458,6 +461,80 @@
         assertFalse(unknownContext.isVariableClassLoaderContext());
     }
 
+    @Test
+    public void testReadVersion1() {
+        String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
+        // Equivalent to
+        //   record(mFooSplit2UsedByOtherApps0);
+        //   record(mFooSecondary1User0);
+        //   record(mFooSecondary2UsedByOtherApps0);
+        //   record(mBarBaseUser0);
+        //   record(mBarSecondary1User0);
+        String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__1\n"
+                + "com.google.foo,1\n"
+                + "#/data/user/0/com.google.foo/sec-1.dex\n"
+                + "0,0," + isa + "\n"
+                + "#/data/user/0/com.google.foo/sec-2.dex\n"
+                + "0,1," + isa + "\n"
+                + "com.google.bar,0\n"
+                + "#/data/user/0/com.google.bar/sec-1.dex\n"
+                + "0,0," + isa + "\n";
+
+        PackageDexUsage packageDexUsage = new PackageDexUsage();
+        try {
+            packageDexUsage.read(new StringReader(content));
+        } catch (IOException e) {
+            fail();
+        }
+
+        // After the read we must sync the data to fill the missing information on the code paths.
+        Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
+        Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+
+        // Handle foo package.
+        packageToUsersMap.put(mFooSplit2UsedByOtherApps0.mPackageName,
+            new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mOwnerUserId)));
+        packageToCodePaths.put(mFooSplit2UsedByOtherApps0.mPackageName,
+            new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mDexFile,
+                mFooSplit1User0.mDexFile, mFooBaseUser0.mDexFile)));
+        // Handle bar package.
+        packageToUsersMap.put(mBarBaseUser0.mPackageName,
+            new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId)));
+        packageToCodePaths.put(mBarBaseUser0.mPackageName,
+            new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
+
+        // Sync the data.
+        packageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
+
+        // Update the class loaders to unknown before asserting if needed. Before version 2 we
+        // didn't have any.
+        String unknown = PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT;
+        TestData fooBaseUser0 = mFooBaseUser0.updateClassLoaderContext(unknown);
+        TestData fooSplit1User0 = mFooSplit1User0.updateClassLoaderContext(unknown);
+        TestData fooSplit2UsedByOtherApps0 =
+            mFooSplit2UsedByOtherApps0.updateClassLoaderContext(unknown);
+        TestData fooSecondary1User0 = mFooSecondary1User0.updateClassLoaderContext(unknown);
+        TestData fooSecondary2UsedByOtherApps0 =
+            mFooSecondary2UsedByOtherApps0.updateClassLoaderContext(unknown);
+        TestData barBaseUser0 = mBarBaseUser0.updateClassLoaderContext(unknown);
+        TestData barSecondary1User0 = mBarSecondary1User0.updateClassLoaderContext(unknown);
+
+        // Assert foo code paths. Note that we ignore the users during upgrade.
+        final Set<String> ignoredUsers = null;
+        assertPackageDexUsage(packageDexUsage, ignoredUsers,
+            fooSplit2UsedByOtherApps0, fooSecondary1User0, fooSecondary2UsedByOtherApps0);
+        // Because fooSplit2UsedByOtherApps0 is used by others, all the other code paths must
+        // share the same data.
+        assertPackageDexUsage(packageDexUsage, ignoredUsers,
+            fooSplit1User0.updateUseByOthers(true),
+            fooSecondary1User0, fooSecondary2UsedByOtherApps0);
+        assertPackageDexUsage(packageDexUsage, ignoredUsers, fooBaseUser0.updateUseByOthers(true),
+            fooSecondary1User0, fooSecondary2UsedByOtherApps0);
+
+        // Assert bar code paths. Note that we ignore the users during upgrade.
+        assertPackageDexUsage(packageDexUsage, ignoredUsers, barBaseUser0, barSecondary1User0);
+    }
+
     private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
         assertPackageDexUsage(mPackageDexUsage, null, primary, secondaries);
     }
@@ -470,9 +547,11 @@
 
         // Check package use info
         assertNotNull(pInfo);
-        assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps());
-        if (users != null) {
-            assertEquals(pInfo.getLoadingPackages(), users);
+        if (primary != null) {
+            assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps(primary.mDexFile));
+            if (users != null) {
+                assertEquals(pInfo.getLoadingPackages(primary.mDexFile), users);
+            }
         }
 
         Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap();
@@ -560,5 +639,10 @@
             return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, mUsedByOtherApps,
                     mPrimaryOrSplit, mUsedBy, newContext);
         }
+
+        private TestData updateUseByOthers(boolean newUsedByOthers) {
+            return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, newUsedByOthers,
+                mPrimaryOrSplit, mUsedBy, mClassLoaderContext);
+        }
     }
 }