Merge "Add parameters for Wifi partial scan trigger"
diff --git a/api/current.txt b/api/current.txt
index 46e3962..c4ddfaf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26534,7 +26534,6 @@
     method public boolean isTdlsSupported();
     method public boolean isWifiEnabled();
     method public deprecated boolean pingSupplicant();
-    method public void queryPasspointIcon(long, java.lang.String);
     method public boolean reassociate();
     method public boolean reconnect();
     method public boolean removeNetwork(int);
@@ -26547,29 +26546,17 @@
     method public boolean startScan();
     method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
     method public int updateNetwork(android.net.wifi.WifiConfiguration);
-    field public static final java.lang.String ACTION_PASSPOINT_DEAUTH_IMMINENT = "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT";
-    field public static final java.lang.String ACTION_PASSPOINT_ICON = "android.net.wifi.action.PASSPOINT_ICON";
-    field public static final java.lang.String ACTION_PASSPOINT_OSU_PROVIDERS_LIST = "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST";
-    field public static final java.lang.String ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION = "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION";
     field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
     field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
     field public static final int ERROR_AUTHENTICATING = 1; // 0x1
-    field public static final java.lang.String EXTRA_ANQP_ELEMENT_DATA = "android.net.wifi.extra.ANQP_ELEMENT_DATA";
     field public static final java.lang.String EXTRA_BSSID = "bssid";
-    field public static final java.lang.String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG";
-    field public static final java.lang.String EXTRA_DELAY = "android.net.wifi.extra.DELAY";
-    field public static final java.lang.String EXTRA_ESS = "android.net.wifi.extra.ESS";
-    field public static final java.lang.String EXTRA_FILENAME = "android.net.wifi.extra.FILENAME";
-    field public static final java.lang.String EXTRA_ICON = "android.net.wifi.extra.ICON";
     field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
     field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi";
     field public static final java.lang.String EXTRA_NEW_STATE = "newState";
     field public static final java.lang.String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
     field public static final java.lang.String EXTRA_RESULTS_UPDATED = "resultsUpdated";
-    field public static final java.lang.String EXTRA_SUBSCRIPTION_REMEDIATION_METHOD = "android.net.wifi.extra.SUBSCRIPTION_REMEDIATION_METHOD";
     field public static final java.lang.String EXTRA_SUPPLICANT_CONNECTED = "connected";
     field public static final java.lang.String EXTRA_SUPPLICANT_ERROR = "supplicantError";
-    field public static final java.lang.String EXTRA_URL = "android.net.wifi.extra.URL";
     field public static final java.lang.String EXTRA_WIFI_INFO = "wifiInfo";
     field public static final java.lang.String EXTRA_WIFI_STATE = "wifi_state";
     field public static final java.lang.String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
@@ -48823,6 +48810,7 @@
     method public void goBack();
     method public void goBackOrForward(int);
     method public void goForward();
+    method public static void initSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
     method public void invokeZoomPicker();
     method public boolean isPrivateBrowsingEnabled();
     method public void loadData(java.lang.String, java.lang.String, java.lang.String);
@@ -48867,6 +48855,7 @@
     method public static void setWebContentsDebuggingEnabled(boolean);
     method public void setWebViewClient(android.webkit.WebViewClient);
     method public deprecated boolean showFindDialog(java.lang.String, boolean);
+    method public static void shutdownSafeBrowsing();
     method public void stopLoading();
     method public void zoomBy(float);
     method public boolean zoomIn();
diff --git a/api/system-current.txt b/api/system-current.txt
index 5694c091..f0f019a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -29066,7 +29066,6 @@
     method public boolean isWifiEnabled();
     method public boolean isWifiScannerSupported();
     method public deprecated boolean pingSupplicant();
-    method public void queryPasspointIcon(long, java.lang.String);
     method public boolean reassociate();
     method public boolean reconnect();
     method public boolean removeNetwork(int);
@@ -29083,10 +29082,6 @@
     method public boolean startScan(android.os.WorkSource);
     method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
     method public int updateNetwork(android.net.wifi.WifiConfiguration);
-    field public static final java.lang.String ACTION_PASSPOINT_DEAUTH_IMMINENT = "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT";
-    field public static final java.lang.String ACTION_PASSPOINT_ICON = "android.net.wifi.action.PASSPOINT_ICON";
-    field public static final java.lang.String ACTION_PASSPOINT_OSU_PROVIDERS_LIST = "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST";
-    field public static final java.lang.String ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION = "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION";
     field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
     field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
     field public static final int CHANGE_REASON_ADDED = 0; // 0x0
@@ -29094,14 +29089,8 @@
     field public static final int CHANGE_REASON_REMOVED = 1; // 0x1
     field public static final java.lang.String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE";
     field public static final int ERROR_AUTHENTICATING = 1; // 0x1
-    field public static final java.lang.String EXTRA_ANQP_ELEMENT_DATA = "android.net.wifi.extra.ANQP_ELEMENT_DATA";
     field public static final java.lang.String EXTRA_BSSID = "bssid";
-    field public static final java.lang.String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG";
     field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason";
-    field public static final java.lang.String EXTRA_DELAY = "android.net.wifi.extra.DELAY";
-    field public static final java.lang.String EXTRA_ESS = "android.net.wifi.extra.ESS";
-    field public static final java.lang.String EXTRA_FILENAME = "android.net.wifi.extra.FILENAME";
-    field public static final java.lang.String EXTRA_ICON = "android.net.wifi.extra.ICON";
     field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
     field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
     field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi";
@@ -29109,10 +29098,8 @@
     field public static final java.lang.String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
     field public static final java.lang.String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
     field public static final java.lang.String EXTRA_RESULTS_UPDATED = "resultsUpdated";
-    field public static final java.lang.String EXTRA_SUBSCRIPTION_REMEDIATION_METHOD = "android.net.wifi.extra.SUBSCRIPTION_REMEDIATION_METHOD";
     field public static final java.lang.String EXTRA_SUPPLICANT_CONNECTED = "connected";
     field public static final java.lang.String EXTRA_SUPPLICANT_ERROR = "supplicantError";
-    field public static final java.lang.String EXTRA_URL = "android.net.wifi.extra.URL";
     field public static final java.lang.String EXTRA_WIFI_AP_STATE = "wifi_state";
     field public static final java.lang.String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
     field public static final java.lang.String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et";
@@ -52468,6 +52455,7 @@
     method public void goBack();
     method public void goBackOrForward(int);
     method public void goForward();
+    method public static void initSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
     method public void invokeZoomPicker();
     method public boolean isPrivateBrowsingEnabled();
     method public void loadData(java.lang.String, java.lang.String, java.lang.String);
@@ -52512,6 +52500,7 @@
     method public static void setWebContentsDebuggingEnabled(boolean);
     method public void setWebViewClient(android.webkit.WebViewClient);
     method public deprecated boolean showFindDialog(java.lang.String, boolean);
+    method public static void shutdownSafeBrowsing();
     method public void stopLoading();
     method public void zoomBy(float);
     method public boolean zoomIn();
@@ -52699,8 +52688,10 @@
     method public abstract java.lang.String findAddress(java.lang.String);
     method public abstract void freeMemoryForTests();
     method public abstract java.lang.String getDefaultUserAgent(android.content.Context);
+    method public abstract void initSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
     method public abstract android.net.Uri[] parseFileChooserResult(int, android.content.Intent);
     method public abstract void setWebContentsDebuggingEnabled(boolean);
+    method public abstract void shutdownSafeBrowsing();
   }
 
   public class WebViewFragment extends android.app.Fragment {
diff --git a/api/test-current.txt b/api/test-current.txt
index f6d432e..a7e384d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -26644,7 +26644,6 @@
     method public boolean isTdlsSupported();
     method public boolean isWifiEnabled();
     method public deprecated boolean pingSupplicant();
-    method public void queryPasspointIcon(long, java.lang.String);
     method public boolean reassociate();
     method public boolean reconnect();
     method public boolean removeNetwork(int);
@@ -26657,29 +26656,17 @@
     method public boolean startScan();
     method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
     method public int updateNetwork(android.net.wifi.WifiConfiguration);
-    field public static final java.lang.String ACTION_PASSPOINT_DEAUTH_IMMINENT = "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT";
-    field public static final java.lang.String ACTION_PASSPOINT_ICON = "android.net.wifi.action.PASSPOINT_ICON";
-    field public static final java.lang.String ACTION_PASSPOINT_OSU_PROVIDERS_LIST = "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST";
-    field public static final java.lang.String ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION = "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION";
     field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
     field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
     field public static final int ERROR_AUTHENTICATING = 1; // 0x1
-    field public static final java.lang.String EXTRA_ANQP_ELEMENT_DATA = "android.net.wifi.extra.ANQP_ELEMENT_DATA";
     field public static final java.lang.String EXTRA_BSSID = "bssid";
-    field public static final java.lang.String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG";
-    field public static final java.lang.String EXTRA_DELAY = "android.net.wifi.extra.DELAY";
-    field public static final java.lang.String EXTRA_ESS = "android.net.wifi.extra.ESS";
-    field public static final java.lang.String EXTRA_FILENAME = "android.net.wifi.extra.FILENAME";
-    field public static final java.lang.String EXTRA_ICON = "android.net.wifi.extra.ICON";
     field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
     field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi";
     field public static final java.lang.String EXTRA_NEW_STATE = "newState";
     field public static final java.lang.String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
     field public static final java.lang.String EXTRA_RESULTS_UPDATED = "resultsUpdated";
-    field public static final java.lang.String EXTRA_SUBSCRIPTION_REMEDIATION_METHOD = "android.net.wifi.extra.SUBSCRIPTION_REMEDIATION_METHOD";
     field public static final java.lang.String EXTRA_SUPPLICANT_CONNECTED = "connected";
     field public static final java.lang.String EXTRA_SUPPLICANT_ERROR = "supplicantError";
-    field public static final java.lang.String EXTRA_URL = "android.net.wifi.extra.URL";
     field public static final java.lang.String EXTRA_WIFI_INFO = "wifiInfo";
     field public static final java.lang.String EXTRA_WIFI_STATE = "wifi_state";
     field public static final java.lang.String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
@@ -45786,6 +45773,7 @@
     method public boolean isAttachedToWindow();
     method public boolean isClickable();
     method public boolean isContextClickable();
+    method public boolean isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
     method public boolean isDirty();
     method public boolean isDrawingCacheEnabled();
     method public boolean isDuplicateParentStateEnabled();
@@ -49222,6 +49210,7 @@
     method public void goBack();
     method public void goBackOrForward(int);
     method public void goForward();
+    method public static void initSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
     method public void invokeZoomPicker();
     method public boolean isPrivateBrowsingEnabled();
     method public void loadData(java.lang.String, java.lang.String, java.lang.String);
@@ -49266,6 +49255,7 @@
     method public static void setWebContentsDebuggingEnabled(boolean);
     method public void setWebViewClient(android.webkit.WebViewClient);
     method public deprecated boolean showFindDialog(java.lang.String, boolean);
+    method public static void shutdownSafeBrowsing();
     method public void stopLoading();
     method public void zoomBy(float);
     method public boolean zoomIn();
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index b60aed6..d71573f 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -63,6 +63,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.StorageManager;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
@@ -1471,7 +1472,8 @@
         }
         ClearDataObserver obs = new ClearDataObserver();
         try {
-            mPm.freeStorageAndNotify(volumeUuid, sizeVal, obs);
+            mPm.freeStorageAndNotify(volumeUuid, sizeVal,
+                    StorageManager.FLAG_ALLOCATE_DEFY_RESERVED, obs);
             synchronized (obs) {
                 while (!obs.finished) {
                     try {
diff --git a/cmds/uiautomator/library/Android.mk b/cmds/uiautomator/library/Android.mk
index e70bd11..4bf856f 100644
--- a/cmds/uiautomator/library/Android.mk
+++ b/cmds/uiautomator/library/Android.mk
@@ -64,10 +64,9 @@
 LOCAL_MODULE := android_uiautomator
 LOCAL_JAVA_LIBRARIES := $(uiautomator.core_java_libraries)
 LOCAL_SOURCE_FILES_ALL_GENERATED := true
-include $(BUILD_STATIC_JAVA_LIBRARY)
 # Make sure to run droiddoc first to generate the stub source files.
-$(full_classes_compiled_jar) : $(uiautomator_stubs_stamp)
-$(built_dex_intermediate) : $(uiautomator_stubs_stamp)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(uiautomator_stubs_stamp)
+include $(BUILD_STATIC_JAVA_LIBRARY)
 
 ###############################################
 # API check
diff --git a/core/java/android/animation/AnimationHandler.java b/core/java/android/animation/AnimationHandler.java
index e2e5a8f..260323f 100644
--- a/core/java/android/animation/AnimationHandler.java
+++ b/core/java/android/animation/AnimationHandler.java
@@ -136,7 +136,8 @@
 
     private void doAnimationFrame(long frameTime) {
         long currentTime = SystemClock.uptimeMillis();
-        for (int i = 0; i < mAnimationCallbacks.size(); i++) {
+        final int size = mAnimationCallbacks.size();
+        for (int i = 0; i < size; i++) {
             final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
             if (callback == null) {
                 continue;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9c90b5b..acceed0 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2112,7 +2112,7 @@
     public void freeStorageAndNotify(String volumeUuid, long idealStorageSize,
             IPackageDataObserver observer) {
         try {
-            mPM.freeStorageAndNotify(volumeUuid, idealStorageSize, observer);
+            mPM.freeStorageAndNotify(volumeUuid, idealStorageSize, 0, observer);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2121,7 +2121,7 @@
     @Override
     public void freeStorage(String volumeUuid, long freeStorageSize, IntentSender pi) {
         try {
-            mPM.freeStorage(volumeUuid, freeStorageSize, pi);
+            mPM.freeStorage(volumeUuid, freeStorageSize, 0, pi);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 23ec260..8c64129 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -89,6 +89,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.ByteOrder;
+import java.util.ArrayList;
 import java.util.Objects;
 
 class ReceiverRestrictedContext extends ContextWrapper {
@@ -417,6 +418,26 @@
         return packagePrefs;
     }
 
+    @Override
+    public void reloadSharedPreferences() {
+        // Build the list of all per-context impls (i.e. caches) we know about
+        ArrayList<SharedPreferencesImpl> spImpls = new ArrayList<>();
+        synchronized (ContextImpl.class) {
+            final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
+            for (int i = 0; i < cache.size(); i++) {
+                final SharedPreferencesImpl sp = cache.valueAt(i);
+                if (sp != null) {
+                    spImpls.add(sp);
+                }
+            }
+        }
+
+        // Issue the reload outside the cache lock
+        for (int i = 0; i < spImpls.size(); i++) {
+            spImpls.get(i).startReloadIfChangedUnexpectedly();
+        }
+    }
+
     /**
      * Try our best to migrate all files from source to target that match
      * requested prefix.
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index fcf0aab..4a0b644 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -28,11 +28,12 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.persistentdata.IPersistentDataBlockService;
 import android.util.Log;
 import android.view.IOnKeyguardExitResult;
 import android.view.IWindowManager;
@@ -40,6 +41,7 @@
 import android.view.WindowManagerGlobal;
 
 import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.widget.LockPatternUtils;
 
 import java.util.List;
 
@@ -74,6 +76,13 @@
             "android.app.action.CONFIRM_DEVICE_CREDENTIAL_WITH_USER";
 
     /**
+     * Intent used to prompt user for factory reset credentials.
+     * @hide
+     */
+    public static final String ACTION_CONFIRM_FRP_CREDENTIAL =
+            "android.app.action.CONFIRM_FRP_CREDENTIAL";
+
+    /**
      * A CharSequence dialog title to show to the user when used with a
      * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
      * @hide
@@ -88,6 +97,23 @@
     public static final String EXTRA_DESCRIPTION = "android.app.extra.DESCRIPTION";
 
     /**
+     * A CharSequence description to show to the user on the alternate button when used with
+     * {@link #ACTION_CONFIRM_FRP_CREDENTIAL}.
+     * @hide
+     */
+    public static final String EXTRA_ALTERNATE_BUTTON_LABEL =
+            "android.app.extra.ALTERNATE_BUTTON_LABEL";
+
+    /**
+     * Result code returned by the activity started by
+     * {@link #createConfirmFactoryResetCredentialIntent} indicating that the user clicked the
+     * alternate button.
+     *
+     * @hide
+     */
+    public static final int RESULT_ALTERNATE = 1;
+
+    /**
      * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
      * for the current user of the device. The caller is expected to launch this activity using
      * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
@@ -130,6 +156,63 @@
         return intent;
     }
 
+    /**
+     * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
+     * for the previous owner of the device. The caller is expected to launch this activity using
+     * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
+     * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
+     *
+     * @param alternateButtonLabel if not empty, a button is provided with the given label. Upon
+     *                             clicking this button, the activity returns
+     *                             {@link #RESULT_ALTERNATE}
+     *
+     * @return  the intent for launching the activity or null if the credential of the previous
+     * owner can not be verified (e.g. because there was none, or the device does not support
+     * verifying credentials after a factory reset, or device setup has already been completed).
+     *
+     * @hide
+     */
+    public Intent createConfirmFactoryResetCredentialIntent(
+            CharSequence title, CharSequence description, CharSequence alternateButtonLabel) {
+        if (!LockPatternUtils.frpCredentialEnabled()) {
+            Log.w(TAG, "Factory reset credentials not supported.");
+            return null;
+        }
+
+        // Cannot verify credential if the device is provisioned
+        if (Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
+            Log.e(TAG, "Factory reset credential cannot be verified after provisioning.");
+            return null;
+        }
+
+        // Make sure we have a credential
+        try {
+            IPersistentDataBlockService pdb = IPersistentDataBlockService.Stub.asInterface(
+                    ServiceManager.getService(Context.PERSISTENT_DATA_BLOCK_SERVICE));
+            if (pdb == null) {
+                Log.e(TAG, "No persistent data block service");
+                return null;
+            }
+            if (!pdb.hasFrpCredentialHandle()) {
+                Log.i(TAG, "The persistent data block does not have a factory reset credential.");
+                return null;
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        Intent intent = new Intent(ACTION_CONFIRM_FRP_CREDENTIAL);
+        intent.putExtra(EXTRA_TITLE, title);
+        intent.putExtra(EXTRA_DESCRIPTION, description);
+        intent.putExtra(EXTRA_ALTERNATE_BUTTON_LABEL, alternateButtonLabel);
+
+        // explicitly set the package for security
+        intent.setPackage(getSettingsPackageForIntent(intent));
+
+        return intent;
+    }
+
     private String getSettingsPackageForIntent(Intent intent) {
         List<ResolveInfo> resolveInfos = mContext.getPackageManager()
                 .queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3ed174b..86ddf9a 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2640,6 +2640,20 @@
     }
 
     /**
+     * @hide
+     */
+    public boolean suppressAlertingDueToGrouping() {
+        if (isGroupSummary()
+                && getGroupAlertBehavior() == Notification.GROUP_ALERT_CHILDREN) {
+            return true;
+        } else if (isGroupChild()
+                && getGroupAlertBehavior() == Notification.GROUP_ALERT_SUMMARY) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Builder class for {@link Notification} objects.
      *
      * Provides a convenient way to set the various fields of a {@link Notification} and generate
@@ -6710,8 +6724,8 @@
                 // Need to clone customContent before adding, because otherwise it can no longer be
                 // parceled independently of remoteViews.
                 customContent = customContent.clone();
-                remoteViews.removeAllViews(R.id.notification_main_column);
-                remoteViews.addView(R.id.notification_main_column, customContent);
+                remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
+                remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
             }
             // also update the end margin if there is an image
             int endMargin = R.dimen.notification_content_margin_end;
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index c076e5e..143d147 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -575,8 +575,8 @@
         setSound(safeUri(parser, ATT_SOUND), safeAudioAttributes(parser));
         enableLights(safeBool(parser, ATT_LIGHTS, false));
         setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR));
-        enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false));
         setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null));
+        enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false));
         setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false));
         setDeleted(safeBool(parser, ATT_DELETED, false));
         setGroup(parser.getAttributeValue(null, ATT_GROUP));
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 5f92af9..42e6147 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -942,6 +942,11 @@
             long ident = Binder.clearCallingIdentity();
 
             if (DEBUG) Log.v(TAG, "doRestore() invoked");
+
+            // Ensure that any side-effect SharedPreferences writes have landed *before*
+            // we may be about to rewrite the file out from underneath
+            waitForSharedPrefs();
+
             BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
             try {
                 BackupAgent.this.onRestore(input, appVersionCode, newState);
@@ -952,8 +957,8 @@
                 Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
                 throw ex;
             } finally {
-                // Ensure that any side-effect SharedPreferences writes have landed
-                waitForSharedPrefs();
+                // And bring live SharedPreferences instances up to date
+                reloadSharedPreferences();
 
                 Binder.restoreCallingIdentity(ident);
                 try {
@@ -1053,6 +1058,8 @@
             } finally {
                 // Ensure that any side-effect SharedPreferences writes have landed
                 waitForSharedPrefs();
+                // And bring live SharedPreferences instances up to date
+                reloadSharedPreferences();
 
                 Binder.restoreCallingIdentity(ident);
                 try {
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 3cb59f2..87e516c 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -35,6 +35,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Objects;
 
 /**
@@ -501,7 +502,7 @@
         if (constraintFlags != j.constraintFlags) {
             return false;
         }
-        if (!Objects.deepEquals(triggerContentUris, j.triggerContentUris)) {
+        if (!Arrays.equals(triggerContentUris, j.triggerContentUris)) {
             return false;
         }
         if (triggerContentUpdateDelay != j.triggerContentUpdateDelay) {
@@ -556,37 +557,37 @@
     public int hashCode() {
         int hashCode = jobId;
         if (extras != null) {
-            hashCode = 31*hashCode + extras.hashCode();
+            hashCode = 31 * hashCode + extras.hashCode();
         }
         if (transientExtras != null) {
-            hashCode = 31*hashCode + transientExtras.hashCode();
+            hashCode = 31 * hashCode + transientExtras.hashCode();
         }
         if (clipData != null) {
-            hashCode = 31*hashCode + clipData.hashCode();
+            hashCode = 31 * hashCode + clipData.hashCode();
         }
         hashCode = 31*hashCode + clipGrantFlags;
         if (service != null) {
-            hashCode = 31*hashCode + service.hashCode();
+            hashCode = 31 * hashCode + service.hashCode();
         }
-        hashCode = 31*hashCode + constraintFlags;
+        hashCode = 31 * hashCode + constraintFlags;
         if (triggerContentUris != null) {
-            hashCode = 31*hashCode + triggerContentUris.hashCode();
+            hashCode = 31 * hashCode + Arrays.hashCode(triggerContentUris);
         }
-        hashCode = 31*hashCode + Long.hashCode(triggerContentUpdateDelay);
-        hashCode = 31*hashCode + Long.hashCode(triggerContentMaxDelay);
-        hashCode = 31*hashCode + Boolean.hashCode(hasEarlyConstraint);
-        hashCode = 31*hashCode + Boolean.hashCode(hasLateConstraint);
-        hashCode = 31*hashCode + networkType;
-        hashCode = 31*hashCode + Long.hashCode(minLatencyMillis);
-        hashCode = 31*hashCode + Long.hashCode(maxExecutionDelayMillis);
-        hashCode = 31*hashCode + Boolean.hashCode(isPeriodic);
-        hashCode = 31*hashCode + Boolean.hashCode(isPersisted);
-        hashCode = 31*hashCode + Long.hashCode(intervalMillis);
-        hashCode = 31*hashCode + Long.hashCode(flexMillis);
-        hashCode = 31*hashCode + Long.hashCode(initialBackoffMillis);
-        hashCode = 31*hashCode + backoffPolicy;
-        hashCode = 31*hashCode + priority;
-        hashCode = 31*hashCode + flags;
+        hashCode = 31 * hashCode + Long.hashCode(triggerContentUpdateDelay);
+        hashCode = 31 * hashCode + Long.hashCode(triggerContentMaxDelay);
+        hashCode = 31 * hashCode + Boolean.hashCode(hasEarlyConstraint);
+        hashCode = 31 * hashCode + Boolean.hashCode(hasLateConstraint);
+        hashCode = 31 * hashCode + networkType;
+        hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
+        hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
+        hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic);
+        hashCode = 31 * hashCode + Boolean.hashCode(isPersisted);
+        hashCode = 31 * hashCode + Long.hashCode(intervalMillis);
+        hashCode = 31 * hashCode + Long.hashCode(flexMillis);
+        hashCode = 31 * hashCode + Long.hashCode(initialBackoffMillis);
+        hashCode = 31 * hashCode + backoffPolicy;
+        hashCode = 31 * hashCode + priority;
+        hashCode = 31 * hashCode + flags;
         return hashCode;
     }
 
diff --git a/core/java/android/app/job/JobServiceEngine.java b/core/java/android/app/job/JobServiceEngine.java
index b0ec650..ab94da8 100644
--- a/core/java/android/app/job/JobServiceEngine.java
+++ b/core/java/android/app/job/JobServiceEngine.java
@@ -210,6 +210,9 @@
      * information.
      */
     public void jobFinished(JobParameters params, boolean needsReschedule) {
+        if (params == null) {
+            throw new NullPointerException("params");
+        }
         Message m = Message.obtain(mHandler, MSG_JOB_FINISHED, params);
         m.arg2 = needsReschedule ? 1 : 0;
         m.sendToTarget();
diff --git a/core/java/android/app/usage/IStorageStatsManager.aidl b/core/java/android/app/usage/IStorageStatsManager.aidl
index 5d1550f..15e5ea5 100644
--- a/core/java/android/app/usage/IStorageStatsManager.aidl
+++ b/core/java/android/app/usage/IStorageStatsManager.aidl
@@ -24,6 +24,7 @@
     boolean isQuotaSupported(String volumeUuid, String callingPackage);
     long getTotalBytes(String volumeUuid, String callingPackage);
     long getFreeBytes(String volumeUuid, String callingPackage);
+    long getCacheBytes(String volumeUuid, String callingPackage);
     long getCacheQuotaBytes(String volumeUuid, int uid, String callingPackage);
     StorageStats queryStatsForPackage(String volumeUuid, String packageName, int userId, String callingPackage);
     StorageStats queryStatsForUid(String volumeUuid, int uid, String callingPackage);
diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java
index d9d958c..0b2b190 100644
--- a/core/java/android/app/usage/StorageStatsManager.java
+++ b/core/java/android/app/usage/StorageStatsManager.java
@@ -142,6 +142,24 @@
         return getFreeBytes(convert(uuid));
     }
 
+    /** {@hide} */
+    public @BytesLong long getCacheBytes(@NonNull UUID storageUuid) throws IOException {
+        try {
+            return mService.getCacheBytes(convert(storageUuid), mContext.getOpPackageName());
+        } catch (ParcelableException e) {
+            e.maybeRethrow(IOException.class);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    @Deprecated
+    public long getCacheBytes(String uuid) throws IOException {
+        return getCacheBytes(convert(uuid));
+    }
+
     /**
      * Return storage statistics for a specific package on the requested storage
      * volume.
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
index f802e8d..914e8fd 100644
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -47,7 +47,9 @@
     private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
     private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
     private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
-    private static final int DATA_TYPE_SERVICE_DATA = 0x16;
+    private static final int DATA_TYPE_SERVICE_DATA_16_BIT = 0x16;
+    private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20;
+    private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21;
     private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
 
     // Flags of the advertising data.
@@ -224,10 +226,16 @@
                     case DATA_TYPE_TX_POWER_LEVEL:
                         txPowerLevel = scanRecord[currentPos];
                         break;
-                    case DATA_TYPE_SERVICE_DATA:
-                        // The first two bytes of the service data are service data UUID in little
-                        // endian. The rest bytes are service data.
+                    case DATA_TYPE_SERVICE_DATA_16_BIT:
+                    case DATA_TYPE_SERVICE_DATA_32_BIT:
+                    case DATA_TYPE_SERVICE_DATA_128_BIT:
                         int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
+                        if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) {
+                         serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT;
+                        } else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) {
+                         serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT;
+                        }
+
                         byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
                                 serviceUuidLength);
                         ParcelUuid serviceDataUuid = BluetoothUuid.parseUuidFrom(
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 61a7be5..9ad33ee 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -813,6 +813,9 @@
      */
     public abstract boolean deleteSharedPreferences(String name);
 
+    /** @hide */
+    public abstract void reloadSharedPreferences();
+
     /**
      * Open a private file associated with this Context's application package
      * for reading.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index e127ca3..20fafb2 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -19,7 +19,6 @@
 import android.annotation.SystemApi;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
-import android.app.Notification;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
@@ -173,6 +172,12 @@
         return mBase.getSharedPreferences(file, mode);
     }
 
+    /** @hide */
+    @Override
+    public void reloadSharedPreferences() {
+        mBase.reloadSharedPreferences();
+    }
+
     @Override
     public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
         return mBase.moveSharedPreferencesFrom(sourceContext, name);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 7712d1e..e800e88 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -361,7 +361,7 @@
      * the operation is completed
      */
      void freeStorageAndNotify(in String volumeUuid, in long freeStorageSize,
-             IPackageDataObserver observer);
+             int storageFlags, IPackageDataObserver observer);
 
     /**
      * Free storage by deleting LRU sorted list of cache files across
@@ -385,7 +385,7 @@
      * to indicate that no call back is desired.
      */
      void freeStorage(in String volumeUuid, in long freeStorageSize,
-             in IntentSender pi);
+             int storageFlags, in IntentSender pi);
 
     /**
      * Delete all the cache files in an applications cache directory
diff --git a/core/java/android/nfc/IAppCallback.aidl b/core/java/android/nfc/IAppCallback.aidl
index c027d54..133146d 100644
--- a/core/java/android/nfc/IAppCallback.aidl
+++ b/core/java/android/nfc/IAppCallback.aidl
@@ -25,6 +25,6 @@
 interface IAppCallback
 {
     BeamShareData createBeamShareData(byte peerLlcpVersion);
-    void onNdefPushComplete(byte peerLlcpVersion);
-    void onTagDiscovered(in Tag tag);
+    oneway void onNdefPushComplete(byte peerLlcpVersion);
+    oneway void onTagDiscovered(in Tag tag);
 }
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 2c9ce3f..093a9b4 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -938,7 +938,7 @@
      */
     void writeToByteBuffer(ByteBuffer buffer, boolean mb, boolean me) {
         boolean sr = mPayload.length < 256;
-        boolean il = mId.length > 0;
+        boolean il = mTnf == TNF_EMPTY ? true : mId.length > 0;
 
         byte flags = (byte)((mb ? FLAG_MB : 0) | (me ? FLAG_ME : 0) |
                 (sr ? FLAG_SR : 0) | (il ? FLAG_IL : 0) | mTnf);
@@ -966,7 +966,7 @@
         int length = 3 + mType.length + mId.length + mPayload.length;
 
         boolean sr = mPayload.length < 256;
-        boolean il = mId.length > 0;
+        boolean il = mTnf == TNF_EMPTY ? true : mId.length > 0;
 
         if (!sr) length += 3;
         if (il) length += 1;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index f361c54..d81ee4e 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1642,11 +1642,20 @@
      */
     @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE)
     @SystemApi
-    public static final int FLAG_ALLOCATE_AGGRESSIVE = 1;
+    public static final int FLAG_ALLOCATE_AGGRESSIVE = 1 << 0;
+
+    /**
+     * Flag indicating that a disk space allocation request should defy any
+     * reserved disk space.
+     *
+     * @hide
+     */
+    public static final int FLAG_ALLOCATE_DEFY_RESERVED = 1 << 1;
 
     /** @hide */
     @IntDef(flag = true, value = {
             FLAG_ALLOCATE_AGGRESSIVE,
+            FLAG_ALLOCATE_DEFY_RESERVED,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AllocateFlags {}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 82b0468e..372ffd6 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9893,13 +9893,49 @@
         public static final String ENABLE_EPHEMERAL_FEATURE = "enable_ephemeral_feature";
 
         /**
-         * The duration for caching uninstalled instant apps.
+         * The min period for caching installed instant apps in milliseconds.
          * <p>
          * Type: long
          * @hide
          */
-        public static final String UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS =
-                "uninstalled_instant_app_cache_duration_millis";
+        public static final String INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
+                "installed_instant_app_min_cache_period";
+
+        /**
+         * The max period for caching installed instant apps in milliseconds.
+         * <p>
+         * Type: long
+         * @hide
+         */
+        public static final String INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
+                "installed_instant_app_max_cache_period";
+
+        /**
+         * The min period for caching uninstalled instant apps in milliseconds.
+         * <p>
+         * Type: long
+         * @hide
+         */
+        public static final String UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
+                "uninstalled_instant_app_min_cache_period";
+
+        /**
+         * The max period for caching uninstalled instant apps in milliseconds.
+         * <p>
+         * Type: long
+         * @hide
+         */
+        public static final String UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
+                "uninstalled_instant_app_max_cache_period";
+
+        /**
+         * The min period for caching unused static shared libs in milliseconds.
+         * <p>
+         * Type: long
+         * @hide
+         */
+        public static final String UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD =
+                "unused_static_shared_lib_min_cache_period";
 
         /**
          * Allows switching users when system user is locked.
diff --git a/core/java/android/service/gatekeeper/IGateKeeperService.aidl b/core/java/android/service/gatekeeper/IGateKeeperService.aidl
index 6db2110..abc6466 100644
--- a/core/java/android/service/gatekeeper/IGateKeeperService.aidl
+++ b/core/java/android/service/gatekeeper/IGateKeeperService.aidl
@@ -78,4 +78,10 @@
      * @param uid the Android user id.
      */
     void clearSecureUserId(int uid);
+
+    /**
+     * Notifies gatekeeper that device setup has been completed and any potentially still existing
+     * state from before a factory reset can be cleaned up (if it has not been already).
+     */
+    void reportDeviceSetupComplete();
 }
diff --git a/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
index 626b408..31352f1 100644
--- a/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
+++ b/core/java/android/service/persistentdata/IPersistentDataBlockService.aidl
@@ -36,5 +36,6 @@
     void setOemUnlockEnabled(boolean enabled);
     boolean getOemUnlockEnabled();
     int getFlashLockState();
+    boolean hasFrpCredentialHandle();
 }
 
diff --git a/core/java/android/text/SpanSet.java b/core/java/android/text/SpanSet.java
index 00f1493..ca25761 100644
--- a/core/java/android/text/SpanSet.java
+++ b/core/java/android/text/SpanSet.java
@@ -98,7 +98,18 @@
      * Similar to {@link Spanned#nextSpanTransition(int, int, Class)}
      */
     int getNextTransition(int start, int limit) {
+        return getNextTransitionSkipping(null, start, limit);
+    }
+
+    /**
+     * Similar to {@link #getNextTransition(int, int)}, but skipping over spans with the exact class
+     * provided.
+     */
+    int getNextTransitionSkipping(Class skip, int start, int limit) {
         for (int i = 0; i < numberOfSpans; i++) {
+            if (spans[i].getClass() == skip) {
+                continue;
+            }
             final int spanStart = spanStarts[i];
             final int spanEnd = spanEnds[i];
             if (spanStart > start && spanStart < limit) limit = spanStart;
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 756e9a0..e287bf7 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -24,6 +24,8 @@
 import android.text.style.CharacterStyle;
 import android.text.style.MetricAffectingSpan;
 import android.text.style.ReplacementSpan;
+import android.text.style.UnderlineSpan;
+import android.util.IntArray;
 import android.util.Log;
 
 import com.android.internal.util.ArrayUtils;
@@ -66,6 +68,8 @@
     private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet =
             new SpanSet<ReplacementSpan>(ReplacementSpan.class);
 
+    private final IntArray mUnderlines = new IntArray();
+
     private static final TextLine[] sCached = new TextLine[3];
 
     /**
@@ -695,6 +699,37 @@
         fmi.leading = Math.max(fmi.leading, previousLeading);
     }
 
+    private static void drawUnderline(TextPaint wp, Canvas c, int color, float thickness,
+            float xstart, float xend, int baseline) {
+        // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h
+        final float underlineTop = baseline + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize();
+
+        final int previousColor = wp.getColor();
+        final Paint.Style previousStyle = wp.getStyle();
+        final boolean previousAntiAlias = wp.isAntiAlias();
+
+        wp.setStyle(Paint.Style.FILL);
+        wp.setAntiAlias(true);
+
+        wp.setColor(color);
+        c.drawRect(xstart, underlineTop, xend, underlineTop + thickness, wp);
+
+        wp.setStyle(previousStyle);
+        wp.setColor(previousColor);
+        wp.setAntiAlias(previousAntiAlias);
+    }
+
+    private float getRunAdvance(TextPaint wp, int start, int end, int contextStart, int contextEnd,
+            boolean runIsRtl, int offset) {
+        if (mCharsValid) {
+            return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset);
+        } else {
+            final int delta = mStart;
+            return wp.getRunAdvance(mText, delta + start, delta + end,
+                    delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
+        }
+    }
+
     /**
      * Utility function for measuring and rendering text.  The text must
      * not include a tab.
@@ -734,14 +769,7 @@
         float ret = 0;
 
         if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) {
-            if (mCharsValid) {
-                ret = wp.getRunAdvance(mChars, start, end, contextStart, contextEnd,
-                        runIsRtl, offset);
-            } else {
-                int delta = mStart;
-                ret = wp.getRunAdvance(mText, delta + start, delta + end,
-                        delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
-            }
+            ret = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset);
         }
 
         if (c != null) {
@@ -762,22 +790,23 @@
             }
 
             if (wp.underlineColor != 0) {
-                // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h
-                float underlineTop = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize();
+                drawUnderline(wp, c, wp.underlineColor, wp.underlineThickness, x, x + ret, y);
+            }
 
-                int previousColor = wp.getColor();
-                Paint.Style previousStyle = wp.getStyle();
-                boolean previousAntiAlias = wp.isAntiAlias();
-
-                wp.setStyle(Paint.Style.FILL);
-                wp.setAntiAlias(true);
-
-                wp.setColor(wp.underlineColor);
-                c.drawRect(x, underlineTop, x + ret, underlineTop + wp.underlineThickness, wp);
-
-                wp.setStyle(previousStyle);
-                wp.setColor(previousColor);
-                wp.setAntiAlias(previousAntiAlias);
+            final int numUnderlines = mUnderlines.size();
+            if (numUnderlines != 0) {
+                // kStdUnderline_Thickness = 1/18, defined in SkTextFormatParams.h
+                final float thickness = (1.0f / 18.0f) * wp.getTextSize();
+                for (int i = 0; i < numUnderlines; i += 2) {
+                    final int underlineStart = Math.max(mUnderlines.get(i), start);
+                    final int underlineEnd = Math.min(mUnderlines.get(i + 1), offset);
+                    final float underlineXStart = getRunAdvance(
+                            wp, start, end, contextStart, contextEnd, runIsRtl, underlineStart);
+                    final float underlineXEnd = getRunAdvance(
+                            wp, start, end, contextStart, contextEnd, runIsRtl, underlineEnd);
+                    drawUnderline(wp, c, wp.getColor(), thickness,
+                            underlineXStart, underlineXEnd, y);
+                }
             }
 
             drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl,
@@ -950,19 +979,27 @@
                 continue;
             }
 
+            mUnderlines.clear();
             for (int j = i, jnext; j < mlimit; j = jnext) {
-                jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + inext) -
-                        mStart;
+                jnext = mCharacterStyleSpanSet.getNextTransitionSkipping(
+                            UnderlineSpan.class, mStart + j, mStart + inext
+                        ) - mStart;
                 int offset = Math.min(jnext, mlimit);
 
                 wp.set(mPaint);
                 for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) {
+                    final int spanStart = mCharacterStyleSpanSet.spanStarts[k];
+                    final int spanEnd = mCharacterStyleSpanSet.spanEnds[k];
                     // Intentionally using >= and <= as explained above
-                    if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + offset) ||
-                            (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue;
+                    if ((spanStart >= mStart + offset) || (spanEnd <= mStart + j)) continue;
 
                     CharacterStyle span = mCharacterStyleSpanSet.spans[k];
-                    span.updateDrawState(wp);
+                    if (span.getClass() == UnderlineSpan.class) {
+                        mUnderlines.add(spanStart);
+                        mUnderlines.add(spanEnd);
+                    } else {
+                        span.updateDrawState(wp);
+                    }
                 }
 
                 wp.setHyphenEdit(adjustHyphenEdit(j, jnext, wp.getHyphenEdit()));
diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java
index adf4938..9afa56d 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -24,7 +24,6 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
-import java.util.List;
 
 /**
  * Class to write to a protobuf stream.
@@ -1593,7 +1592,7 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_FIXED64);
 
-        writeRepeatedFixed64(id, val);
+        writeRepeatedFixed64Impl(id, val);
     }
 
     private void writeRepeatedFixed64Impl(int id, long val) {
@@ -1720,7 +1719,7 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_SFIXED64);
 
-        writeRepeatedSFixed64(id, val);
+        writeRepeatedSFixed64Impl(id, val);
     }
 
     private void writeRepeatedSFixed64Impl(int id, long val) {
@@ -1785,7 +1784,7 @@
         assertNotCompacted();
         final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_BOOL);
 
-        writeRepeatedBool(id, val);
+        writeRepeatedBoolImpl(id, val);
     }
 
     private void writeRepeatedBoolImpl(int id, boolean val) {
diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl
index dbeb747..d2dcb568 100644
--- a/core/java/android/view/IPinnedStackController.aidl
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -30,6 +30,14 @@
     oneway void setIsMinimized(boolean isMinimized);
 
     /**
+     * Notifies the controller of the current min edge size, this is needed to allow the system to
+     * properly calculate the aspect ratio of the expanded PIP.  The given {@param minEdgeSize} is
+     * always bounded to be larger than the default minEdgeSize, so the caller can call this method
+     * with 0 to reset to the default size.
+     */
+    oneway void setMinEdgeSize(int minEdgeSize);
+
+    /**
      * @return what WM considers to be the current device rotation.
      */
     int getDisplayRotation();
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index f987e4e..ad46d07 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -923,8 +923,11 @@
                             + " include tag: <include layout=\"@layout/layoutID\" />");
                 }
 
-                // Attempt to resolve the "?attr/name" string to an identifier.
-                layout = context.getResources().getIdentifier(value.substring(1), null, null);
+                // Attempt to resolve the "?attr/name" string to an attribute
+                // within the default (e.g. application) package.
+                layout = context.getResources().getIdentifier(
+                        value.substring(1), "attr", context.getPackageName());
+
             }
 
             // The layout might be referencing a theme attribute.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 2c9b2e4..b57ac66 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -31,7 +31,7 @@
 import android.graphics.Region;
 import android.os.Build;
 import android.os.Handler;
-import android.os.Message;
+import android.os.Looper;
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -120,34 +120,11 @@
     final Rect mTmpRect = new Rect();
     final Configuration mConfiguration = new Configuration();
 
-    static final int KEEP_SCREEN_ON_MSG = 1;
-    static final int DRAW_FINISHED_MSG = 2;
-
     int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
 
     boolean mIsCreating = false;
     private volatile boolean mRtHandlingPositionUpdates = false;
 
-    final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case KEEP_SCREEN_ON_MSG: {
-                    setKeepScreenOn(msg.arg1 != 0);
-                } break;
-                case DRAW_FINISHED_MSG: {
-                    mDrawFinished = true;
-                    if (mAttachedToWindow) {
-                        mParent.requestTransparentRegion(SurfaceView.this);
-
-                        notifyDrawFinished();
-                        invalidate();
-                    }
-                } break;
-            }
-        }
-    };
-
     private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
             = new ViewTreeObserver.OnScrollChangedListener() {
                     @Override
@@ -287,6 +264,22 @@
         updateSurface();
     }
 
+    private void performDrawFinished() {
+        if (mPendingReportDraws > 0) {
+            mDrawFinished = true;
+            if (mAttachedToWindow) {
+                mParent.requestTransparentRegion(SurfaceView.this);
+                
+                notifyDrawFinished();
+                invalidate();
+            }
+        } else {
+            Log.e(TAG, System.identityHashCode(this) + "finished drawing"
+                    + " but no pending report draw (extra call"
+                    + " to draw completion runnable?)");
+        }
+    }
+
     void notifyDrawFinished() {
         ViewRootImpl viewRoot = getViewRootImpl();
         if (viewRoot != null) {
@@ -751,7 +744,9 @@
             mDeferredDestroySurfaceControl = null;
         }
 
-        mHandler.sendEmptyMessage(DRAW_FINISHED_MSG);
+        runOnUiThread(() -> {
+            performDrawFinished();
+        });
     }
 
     private void setParentSpaceRectangle(Rect position, long frameNumber) {
@@ -880,6 +875,15 @@
                 + "type=" + type, new Throwable());
     }
 
+    private void runOnUiThread(Runnable runnable) {
+        Handler handler = getHandler();
+        if (handler != null && handler.getLooper() != Looper.myLooper()) {
+            handler.post(runnable);
+        } else {
+            runnable.run();
+        }
+    }
+
     /**
      * Check to see if the surface has fixed size dimensions or if the surface's
      * dimensions are dimensions are dependent on its current layout.
@@ -960,9 +964,7 @@
 
         @Override
         public void setKeepScreenOn(boolean screenOn) {
-            Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG);
-            msg.arg1 = screenOn ? 1 : 0;
-            mHandler.sendMessage(msg);
+            runOnUiThread(() -> SurfaceView.this.setKeepScreenOn(screenOn));
         }
 
         /**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 107bc94..84baeb7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -964,115 +964,155 @@
     private static final int[] VISIBILITY_FLAGS = {VISIBLE, INVISIBLE, GONE};
 
     /**
-     * This view contains an email address.
+     * Hint indicating that this view can be autofilled with an email address.
      *
-     * Use with {@link #setAutofillHints(String[])}, or set "{@value #AUTOFILL_HINT_EMAIL_ADDRESS}"
-     * to <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
+     * <p>Can be used with either {@link #setAutofillHints(String[])} or
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
+     * value should be <code>{@value #AUTOFILL_HINT_EMAIL_ADDRESS}</code>).
+     *
+     * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
      */
     public static final String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
 
     /**
-     * The view contains a real name.
+     * Hint indicating that this view can be autofilled with a user's real name.
      *
-     * Use with {@link #setAutofillHints(String[])}, or set "{@value #AUTOFILL_HINT_NAME}" to
-     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
+     * <p>Can be used with either {@link #setAutofillHints(String[])} or
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
+     * value should be <code>{@value #AUTOFILL_HINT_NAME}</code>).
+     *
+     * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
      */
     public static final String AUTOFILL_HINT_NAME = "name";
 
     /**
-     * The view contains a user name.
+     * Hint indicating that this view can be autofilled with a username.
      *
-     * Use with {@link #setAutofillHints(String[])}, or set "{@value #AUTOFILL_HINT_USERNAME}" to
-     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
+     * <p>Can be used with either {@link #setAutofillHints(String[])} or
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
+     * value should be <code>{@value #AUTOFILL_HINT_USERNAME}</code>).
+     *
+     * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
      */
     public static final String AUTOFILL_HINT_USERNAME = "username";
 
     /**
-     * The view contains a password.
+     * Hint indicating that this view can be autofilled with a password.
      *
-     * Use with {@link #setAutofillHints(String[])}, or set "{@value #AUTOFILL_HINT_PASSWORD}" to
-     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
+     * <p>Can be used with either {@link #setAutofillHints(String[])} or
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
+     * value should be <code>{@value #AUTOFILL_HINT_PASSWORD}</code>).
+     *
+     * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
      */
     public static final String AUTOFILL_HINT_PASSWORD = "password";
 
     /**
-     * The view contains a phone number.
+     * Hint indicating that this view can be autofilled with a phone number.
      *
-     * Use with {@link #setAutofillHints(String[])}, or set "{@value #AUTOFILL_HINT_PHONE}" to
-     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
+     * <p>Can be used with either {@link #setAutofillHints(String[])} or
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
+     * value should be <code>{@value #AUTOFILL_HINT_PHONE}</code>).
+     *
+     * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
      */
     public static final String AUTOFILL_HINT_PHONE = "phone";
 
     /**
-     * The view contains a postal address.
+     * Hint indicating that this view can be autofilled with a postal address.
      *
-     * Use with {@link #setAutofillHints(String[])}, or set "{@value #AUTOFILL_HINT_POSTAL_ADDRESS}"
-     * to <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
+     * <p>Can be used with either {@link #setAutofillHints(String[])} or
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
+     * value should be <code>{@value #AUTOFILL_HINT_POSTAL_ADDRESS}</code>).
+     *
+     * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
      */
     public static final String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
 
     /**
-     * The view contains a postal code.
+     * Hint indicating that this view can be autofilled with a postal code.
      *
-     * Use with {@link #setAutofillHints(String[])}, or set "{@value #AUTOFILL_HINT_POSTAL_CODE}" to
-     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
+     * <p>Can be used with either {@link #setAutofillHints(String[])} or
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
+     * value should be <code>{@value #AUTOFILL_HINT_POSTAL_CODE}</code>).
+     *
+     * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
      */
     public static final String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
 
     /**
-     * The view contains a credit card number.
+     * Hint indicating that this view can be autofilled with a credit card number.
      *
-     * Use with {@link #setAutofillHints(String[])}, or set "{@value
-     * #AUTOFILL_HINT_CREDIT_CARD_NUMBER}" to <a href="#attr_android:autofillHint"> {@code
-     * android:autofillHint}.
+     * <p>Can be used with either {@link #setAutofillHints(String[])} or
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
+     * value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_NUMBER}</code>).
+     *
+     * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
      */
     public static final String AUTOFILL_HINT_CREDIT_CARD_NUMBER = "creditCardNumber";
 
     /**
-     * The view contains a credit card security code.
+     * Hint indicating that this view can be autofilled with a credit card security code.
      *
-     * Use with {@link #setAutofillHints(String[])}, or set "{@value
-     * #AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE}" to <a href="#attr_android:autofillHint"> {@code
-     * android:autofillHint}.
+     * <p>Can be used with either {@link #setAutofillHints(String[])} or
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
+     * value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE}</code>).
+     *
+     * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
      */
     public static final String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
 
     /**
-     * The view contains a credit card expiration date.
+     * Hint indicating that this view can be autofilled with a credit card expiration date.
      *
-     * Use with {@link #setAutofillHints(String[])}, or set "{@value
-     * #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE}" to <a href="#attr_android:autofillHint"> {@code
-     * android:autofillHint}.
+     * <p>It should be used when the credit card expiration date is represented by just one view;
+     * if it is represented by more than one (for example, one view for the month and another view
+     * for the year), then each of these views should use the hint specific for the unit
+     * ({@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY},
+     * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH},
+     * or {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}).
+     *
+     * <p>Can be used with either {@link #setAutofillHints(String[])} or
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
+     * value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE}</code>).
+     *
+     * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
      */
     public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE =
             "creditCardExpirationDate";
 
     /**
-     * The view contains the month a credit card expires.
+     * Hint indicating that this view can be autofilled with a credit card expiration month.
      *
-     * Use with {@link #setAutofillHints(String[])}, or set "{@value
-     * #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH}" to <a href="#attr_android:autofillHint"> {@code
-     * android:autofillHint}.
+     * <p>Can be used with either {@link #setAutofillHints(String[])} or
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
+     * value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH}</code>).
+     *
+     * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
      */
     public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH =
             "creditCardExpirationMonth";
 
     /**
-     * The view contains the year a credit card expires.
+     * Hint indicating that this view can be autofilled with a credit card expiration year.
      *
-     * Use with {@link #setAutofillHints(String[])}, or set "{@value
-     * #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}" to <a href="#attr_android:autofillHint"> {@code
-     * android:autofillHint}.
+     * <p>Can be used with either {@link #setAutofillHints(String[])} or
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
+     * value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}</code>).
+     *
+     * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
      */
     public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR =
             "creditCardExpirationYear";
 
     /**
-     * The view contains the day a credit card expires.
+     * Hint indicating that this view can be autofilled with a credit card expiration day.
      *
-     * Use with {@link #setAutofillHints(String[])}, or set "{@value
-     * #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY}" to <a href="#attr_android:autofillHint"> {@code
-     * android:autofillHint}.
+     * <p>Can be used with either {@link #setAutofillHints(String[])} or
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
+     * value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY}</code>).
+     *
+     * <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
      */
     public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = "creditCardExpirationDay";
 
@@ -7581,9 +7621,13 @@
     }
 
     /**
-     * Describes the content of a view so that a autofill service can fill in the appropriate data.
+     * Gets the hints that help an {@link android.service.autofill.AutofillService} determine how
+     * to autofill the view with the user's data.
      *
-     * @return The hints set via the attribute or {@code null} if no hint it set.
+     * <p>See {@link #setAutofillHints(String...)} for more info about these hints.
+     *
+     * @return The hints set via the attribute or {@link #setAutofillHints(String...)}, or
+     * {@code null} if no hints were set.
      *
      * @attr ref android.R.styleable#View_autofillHints
      */
@@ -9248,8 +9292,26 @@
     }
 
     /**
-     * Sets the hints that helps the autofill service to select the appropriate data to fill the
-     * view.
+     * Sets the hints that help an {@link android.service.autofill.AutofillService} determine how
+     * to autofill the view with the user's data.
+     *
+     * <p>Typically, there is only one way to autofill a view, but there could be more than one.
+     * For example, if the application accepts either an username or email address to identify
+     * an user.
+     *
+     * <p>These hints are not validated by the Android System, but passed "as is" to the service.
+     * Hence, they can have any value, but it's recommended to use the {@code AUTOFILL_HINT_}
+     * constants such as:
+     * {@link #AUTOFILL_HINT_USERNAME}, {@link #AUTOFILL_HINT_PASSWORD},
+     * {@link #AUTOFILL_HINT_EMAIL_ADDRESS},
+     * {@link #AUTOFILL_HINT_NAME},
+     * {@link #AUTOFILL_HINT_PHONE},
+     * {@link #AUTOFILL_HINT_POSTAL_ADDRESS}, {@link #AUTOFILL_HINT_POSTAL_CODE},
+     * {@link #AUTOFILL_HINT_CREDIT_CARD_NUMBER}, {@link #AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE},
+     * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE},
+     * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY},
+     * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH} or
+     * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}.
      *
      * @param autofillHints The autofill hints to set. If the array is emtpy, {@code null} is set.
      * @attr ref android.R.styleable#View_autofillHints
@@ -19817,18 +19879,23 @@
      * Check whether we need to draw a default focus highlight when this view gets focused,
      * which requires:
      * <ul>
-     *     <li>In the background, {@link android.R.attr#state_focused} is not defined.</li>
+     *     <li>In both background and foreground, {@link android.R.attr#state_focused}
+     *         is not defined.</li>
      *     <li>This view is not in touch mode.</li>
      *     <li>This view doesn't opt out for a default focus highlight, via
      *         {@link #setDefaultFocusHighlightEnabled(boolean)}.</li>
      *     <li>This view is attached to window.</li>
      * </ul>
      * @return {@code true} if a default focus highlight is needed.
+     * @hide
      */
-    private boolean isDefaultFocusHighlightNeeded(Drawable background) {
-        final boolean hasFocusStateSpecified = background == null || !background.isStateful()
-                || !background.hasFocusStateSpecified();
-        return !isInTouchMode() && getDefaultFocusHighlightEnabled() && hasFocusStateSpecified
+    @TestApi
+    public boolean isDefaultFocusHighlightNeeded(Drawable background, Drawable foreground) {
+        final boolean lackFocusState = (background == null || !background.isStateful()
+                || !background.hasFocusStateSpecified())
+                && (foreground == null || !foreground.isStateful()
+                || !foreground.hasFocusStateSpecified());
+        return !isInTouchMode() && getDefaultFocusHighlightEnabled() && lackFocusState
                 && isAttachedToWindow() && sUseDefaultFocusHighlight;
     }
 
@@ -19840,7 +19907,8 @@
      */
     private void switchDefaultFocusHighlight() {
         if (isFocused()) {
-            final boolean needed = isDefaultFocusHighlightNeeded(mBackground);
+            final boolean needed = isDefaultFocusHighlightNeeded(mBackground,
+                    mForegroundInfo == null ? null : mForegroundInfo.mDrawable);
             final boolean active = mDefaultFocusHighlight != null;
             if (needed && !active) {
                 setDefaultFocusHighlight(getDefaultFocusHighlightDrawable());
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ded25ab..6450b57 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2330,7 +2330,7 @@
 
         // Remember if we must report the next draw.
         if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
-            mReportNextDraw = true;
+            reportNextDraw();
         }
 
         boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
@@ -2731,11 +2731,8 @@
     /**
      * A count of the number of calls to pendingDrawFinished we
      * require to notify the WM drawing is complete.
-     *
-     * This starts at 1, for the ViewRootImpl surface itself.
-     * Subsurfaces may debt the value with drawPending.
      */
-    int mDrawsNeededToReport = 1;
+    int mDrawsNeededToReport = 0;
 
     /**
      * Delay notifying WM of draw finished until
@@ -2761,7 +2758,7 @@
 
     private void reportDrawFinished() {
         try {
-            mDrawsNeededToReport = 1;
+            mDrawsNeededToReport = 0;
             mWindowSession.finishDrawing(mWindow);
         } catch (RemoteException e) {
             // Have fun!
@@ -3772,13 +3769,12 @@
                     args.recycle();
 
                     if (msg.what == MSG_RESIZED_REPORT) {
-                        mReportNextDraw = true;
+                        reportNextDraw();
                     }
 
                     if (mView != null && framesChanged) {
                         forceLayout(mView);
                     }
-
                     requestLayout();
                 }
                 break;
@@ -7351,6 +7347,14 @@
         return false;
     }
 
+
+    private void reportNextDraw() {
+        if (mReportNextDraw == false) {
+            drawPending();
+        }
+        mReportNextDraw = true;
+    }
+
     /**
      * Force the window to report its next draw.
      * <p>
@@ -7360,7 +7364,7 @@
      * @hide
      */
     public void setReportNextDraw() {
-        mReportNextDraw = true;
+        reportNextDraw();
         invalidate();
     }
 
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index bf0e10f..55aed52 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -225,6 +225,9 @@
      */
     public abstract boolean isKeyguardLocked();
 
+    /** @return {@code true} if the keyguard is going away. */
+    public abstract boolean isKeyguardGoingAway();
+
     /**
      * Gets the frame of a window given its token.
      *
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 330aae9..9dd0fb0 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -1127,7 +1127,7 @@
         record.mParcelableData = parcel.readParcelable(null);
         parcel.readList(record.mText, null);
         record.mSourceWindowId = parcel.readInt();
-        record.mSourceNode = parcel.readParcelable(null);
+        record.mSourceNodeId = parcel.readLong();
         record.mSealed = (parcel.readInt() == 1);
     }
 
@@ -1179,10 +1179,7 @@
         parcel.writeParcelable(record.mParcelableData, flags);
         parcel.writeList(record.mText);
         parcel.writeInt(record.mSourceWindowId);
-        // create copy of the node here because the node would be recycled just after it is written
-        // to parcel
-        parcel.writeParcelable(record.mSourceNode != null ?
-                AccessibilityNodeInfo.obtain(record.mSourceNode) : null, flags);
+        parcel.writeLong(record.mSourceNodeId);
         parcel.writeInt(record.mSealed ? 1 : 0);
     }
 
@@ -1207,9 +1204,7 @@
             builder.append("; ContentChangeTypes: ").append(
                     contentChangeTypesToString(mContentChangeTypes));
             builder.append("; sourceWindowId: ").append(mSourceWindowId);
-            if (mSourceNode != null) {
-                builder.append("; mSourceNodeId: ").append(mSourceNode.getSourceNodeId());
-            }
+            builder.append("; mSourceNodeId: ").append(mSourceNodeId);
             for (int i = 0; i < getRecordCount(); i++) {
                 final AccessibilityRecord record = getRecord(i);
                 builder.append("  Record ");
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 3bf8d27..82a6de7 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -90,7 +90,6 @@
     /** @hide */
     public static final int UNDEFINED_SELECTION_INDEX = -1;
 
-    /* Special IDs for node source IDs */
     /** @hide */
     public static final int UNDEFINED_ITEM_ID = Integer.MAX_VALUE;
 
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 3f1fece..c6057ec 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -16,6 +16,7 @@
 
 package android.view.accessibility;
 
+import android.annotation.Nullable;
 import android.os.Parcelable;
 import android.view.View;
 
@@ -90,7 +91,7 @@
 
     int mAddedCount= UNDEFINED;
     int mRemovedCount = UNDEFINED;
-    AccessibilityNodeInfo mSourceNode;
+    long mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
     int mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
 
     CharSequence mClassName;
@@ -116,7 +117,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setSource(View source) {
-        setSource(source, UNDEFINED);
+        setSource(source, AccessibilityNodeInfo.UNDEFINED_ITEM_ID);
     }
 
     /**
@@ -133,46 +134,28 @@
      * @param root The root of the virtual subtree.
      * @param virtualDescendantId The id of the virtual descendant.
      */
-    public void setSource(View root, int virtualDescendantId) {
+    public void setSource(@Nullable View root, int virtualDescendantId) {
         enforceNotSealed();
         boolean important = true;
+        int rootViewId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
         mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
-        clearSourceNode();
         if (root != null) {
-            if (virtualDescendantId == View.NO_ID
-                    || virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID
-                    || virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
-                important = root.isImportantForAccessibility();
-                mSourceNode = root.createAccessibilityNodeInfo();
-            } else {
-                AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
-                if (provider != null) {
-                    mSourceNode = provider.createAccessibilityNodeInfo(virtualDescendantId);
-                }
-            }
-
+            important = root.isImportantForAccessibility();
+            rootViewId = root.getAccessibilityViewId();
             mSourceWindowId = root.getAccessibilityWindowId();
         }
         setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, important);
+        mSourceNodeId = AccessibilityNodeInfo.makeNodeId(rootViewId, virtualDescendantId);
     }
 
     /**
-     * Set the source directly to an AccessibilityNodeInfo rather than indirectly via a View
+     * Set the source node ID directly
      *
-     * @param info The source
-     *
+     * @param sourceNodeId The source node Id
      * @hide
      */
-    public void setSource(AccessibilityNodeInfo info) {
-        enforceNotSealed();
-        clearSourceNode();
-        mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
-        if (info != null) {
-            mSourceNode = AccessibilityNodeInfo.obtain(info);
-            setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY,
-                    mSourceNode.isImportantForAccessibility());
-            mSourceWindowId = info.getWindowId();
-        }
+    public void setSourceNodeId(long sourceNodeId) {
+        mSourceNodeId = sourceNodeId;
     }
 
     /**
@@ -186,11 +169,15 @@
      */
     public AccessibilityNodeInfo getSource() {
         enforceSealed();
-        if (mSourceNode != null) {
-            return AccessibilityNodeInfo.obtain(mSourceNode);
+        if ((mConnectionId == UNDEFINED)
+                || (mSourceWindowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)
+                || (AccessibilityNodeInfo.getAccessibilityViewId(mSourceNodeId)
+                        == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)) {
+            return null;
         }
-
-        return null;
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mSourceWindowId,
+                mSourceNodeId, false, GET_SOURCE_PREFETCH_FLAGS, null);
     }
 
     /**
@@ -647,7 +634,7 @@
      * @hide
      */
     public long getSourceNodeId() {
-        return mSourceNode != null ? mSourceNode.getSourceNodeId() : UNDEFINED;
+        return mSourceNodeId;
     }
 
     /**
@@ -661,9 +648,6 @@
     public void setConnectionId(int connectionId) {
         enforceNotSealed();
         mConnectionId = connectionId;
-        if (mSourceNode != null) {
-            mSourceNode.setConnectionId(mConnectionId);
-        }
     }
 
     /**
@@ -675,9 +659,6 @@
      */
     public void setSealed(boolean sealed) {
         mSealed = sealed;
-        if (mSourceNode != null) {
-            mSourceNode.setSealed(sealed);
-        }
     }
 
     /**
@@ -816,9 +797,7 @@
         mParcelableData = record.mParcelableData;
         mText.addAll(record.mText);
         mSourceWindowId = record.mSourceWindowId;
-        if (record.mSourceNode != null) {
-            mSourceNode = AccessibilityNodeInfo.obtain(record.mSourceNode);
-        }
+        mSourceNodeId = record.mSourceNodeId;
         mConnectionId = record.mConnectionId;
     }
 
@@ -843,19 +822,11 @@
         mBeforeText = null;
         mParcelableData = null;
         mText.clear();
-        clearSourceNode();
-        mSourceWindowId = UNDEFINED;
+        mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
+        mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
         mConnectionId = UNDEFINED;
     }
 
-    private void clearSourceNode() {
-        if (mSourceNode != null) {
-            mSourceNode.recycle();
-            mSourceNode = null;
-        }
-
-    }
-
     @Override
     public String toString() {
         StringBuilder builder = new StringBuilder();
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index dda5df6..cc14542 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -31,15 +31,15 @@
 import android.graphics.Picture;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.net.http.SslCertificate;
 import android.net.Uri;
+import android.net.http.SslCertificate;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.StrictMode;
 import android.os.RemoteException;
+import android.os.StrictMode;
 import android.print.PrintDocumentAdapter;
 import android.security.KeyChain;
 import android.util.AttributeSet;
@@ -49,10 +49,10 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewStructure;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewHierarchyEncoder;
+import android.view.ViewStructure;
 import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -1621,6 +1621,23 @@
     }
 
     /**
+     * Starts Safe Browsing initialization. This should only be called once.
+     * @param context is the activity context the WebView will be used in.
+     * @param callback will be called with the value true if initialization is
+     * successful. The callback will be run on the UI thread.
+     */
+    public static void initSafeBrowsing(Context context, ValueCallback<Boolean> callback) {
+        getFactory().getStatics().initSafeBrowsing(context, callback);
+    }
+
+    /**
+     * Shuts down Safe Browsing. This should only be called once.
+     */
+    public static void shutdownSafeBrowsing() {
+        getFactory().getStatics().shutdownSafeBrowsing();
+    }
+
+    /**
      * Gets the WebBackForwardList for this WebView. This contains the
      * back/forward list for use in querying each item in the history stack.
      * This is a copy of the private WebBackForwardList so it contains only a
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 8359a10..5cf48b5 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -74,6 +74,18 @@
          * {@link android.webkit.WebChromeClient.FileChooserParams#parseResult(int, Intent)}
          */
         Uri[] parseFileChooserResult(int resultCode, Intent intent);
+
+        /**
+         * Implement the API method
+         * {@link android.webkit.WebView#initSafeBrowsing(Context , ValueCallback<Boolean>)}
+         */
+        void initSafeBrowsing(Context context, ValueCallback<Boolean> callback);
+
+        /**
+         * Implement the API method
+         * {@link android.webkit.WebView#shutdownSafeBrowsing()}
+         */
+        void shutdownSafeBrowsing();
     }
 
     Statics getStatics();
diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java
index 9a39a17..121a8c5 100644
--- a/core/java/android/widget/ActivityChooserView.java
+++ b/core/java/android/widget/ActivityChooserView.java
@@ -24,6 +24,8 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.database.DataSetObserver;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -403,6 +405,7 @@
             }
             popupWindow.getListView().setContentDescription(mContext.getString(
                     R.string.activitychooserview_choose_application));
+            popupWindow.getListView().setSelector(new ColorDrawable(Color.TRANSPARENT));
         }
     }
 
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 57818e2..1dc5b44 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -19,6 +19,7 @@
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -1640,4 +1641,13 @@
         super.encodeProperties(stream);
         stream.addProperty("layout:baseline", getBaseline());
     }
+
+    /** @hide */
+    @Override
+    @TestApi
+    public boolean isDefaultFocusHighlightNeeded(Drawable background, Drawable foreground) {
+        final boolean lackFocusState = mDrawable == null || !mDrawable.isStateful()
+                || !mDrawable.hasFocusStateSpecified();
+        return super.isDefaultFocusHighlightNeeded(background, foreground) && lackFocusState;
+    }
 }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index e534233..985584e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -99,6 +99,26 @@
      */
     private static final int MAX_NESTED_VIEWS = 10;
 
+    // The unique identifiers for each custom {@link Action}.
+    private static final int SET_ON_CLICK_PENDING_INTENT_TAG = 1;
+    private static final int REFLECTION_ACTION_TAG = 2;
+    private static final int SET_DRAWABLE_PARAMETERS_TAG = 3;
+    private static final int VIEW_GROUP_ACTION_ADD_TAG = 4;
+    private static final int SET_REFLECTION_ACTION_WITHOUT_PARAMS_TAG = 5;
+    private static final int SET_EMPTY_VIEW_ACTION_TAG = 6;
+    private static final int VIEW_GROUP_ACTION_REMOVE_TAG = 7;
+    private static final int SET_PENDING_INTENT_TEMPLATE_TAG = 8;
+    private static final int SET_ON_CLICK_FILL_IN_INTENT_TAG = 9;
+    private static final int SET_REMOTE_VIEW_ADAPTER_INTENT_TAG = 10;
+    private static final int TEXT_VIEW_DRAWABLE_ACTION_TAG = 11;
+    private static final int BITMAP_REFLECTION_ACTION_TAG = 12;
+    private static final int TEXT_VIEW_SIZE_ACTION_TAG = 13;
+    private static final int VIEW_PADDING_ACTION_TAG = 14;
+    private static final int SET_REMOTE_VIEW_ADAPTER_LIST_TAG = 15;
+    private static final int TEXT_VIEW_DRAWABLE_COLOR_FILTER_ACTION_TAG = 17;
+    private static final int SET_REMOTE_INPUTS_ACTION_TAG = 18;
+    private static final int LAYOUT_PARAM_ACTION_TAG = 19;
+
     /**
      * Application that hosts the remote views.
      *
@@ -441,8 +461,6 @@
         int viewId;
         int emptyViewId;
 
-        public final static int TAG = 6;
-
         SetEmptyView(int viewId, int emptyViewId) {
             this.viewId = viewId;
             this.emptyViewId = emptyViewId;
@@ -454,7 +472,7 @@
         }
 
         public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(TAG);
+            out.writeInt(SET_EMPTY_VIEW_ACTION_TAG);
             out.writeInt(this.viewId);
             out.writeInt(this.emptyViewId);
         }
@@ -489,7 +507,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_ON_CLICK_FILL_IN_INTENT_TAG);
             dest.writeInt(viewId);
             fillInIntent.writeToParcel(dest, 0 /* no flags */);
         }
@@ -555,8 +573,6 @@
         }
 
         Intent fillInIntent;
-
-        public final static int TAG = 9;
     }
 
     private class SetPendingIntentTemplate extends Action {
@@ -571,7 +587,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_PENDING_INTENT_TEMPLATE_TAG);
             dest.writeInt(viewId);
             pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */);
         }
@@ -632,8 +648,6 @@
         }
 
         PendingIntent pendingIntentTemplate;
-
-        public final static int TAG = 8;
     }
 
     private class SetRemoteViewsAdapterList extends Action {
@@ -656,7 +670,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_REMOTE_VIEW_ADAPTER_LIST_TAG);
             dest.writeInt(viewId);
             dest.writeInt(viewTypeCount);
 
@@ -715,7 +729,6 @@
 
         int viewTypeCount;
         ArrayList<RemoteViews> list;
-        public final static int TAG = 15;
     }
 
     private class SetRemoteViewsAdapterIntent extends Action {
@@ -730,7 +743,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_REMOTE_VIEW_ADAPTER_INTENT_TAG);
             dest.writeInt(viewId);
             intent.writeToParcel(dest, flags);
         }
@@ -782,8 +795,6 @@
 
         Intent intent;
         boolean isAsync = false;
-
-        public final static int TAG = 10;
     }
 
     /**
@@ -807,7 +818,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_ON_CLICK_PENDING_INTENT_TAG);
             dest.writeInt(viewId);
 
             // We use a flag to indicate whether the parcel contains a valid object.
@@ -860,8 +871,6 @@
         }
 
         PendingIntent pendingIntent;
-
-        public final static int TAG = 1;
     }
 
     private static Rect getSourceBounds(View v) {
@@ -997,7 +1006,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_DRAWABLE_PARAMETERS_TAG);
             dest.writeInt(viewId);
             dest.writeInt(targetBackground ? 1 : 0);
             dest.writeInt(alpha);
@@ -1048,15 +1057,11 @@
         int colorFilter;
         PorterDuff.Mode filterMode;
         int level;
-
-        public final static int TAG = 3;
     }
 
     private final class ReflectionActionWithoutParams extends Action {
         final String methodName;
 
-        public final static int TAG = 5;
-
         ReflectionActionWithoutParams(int viewId, String methodName) {
             this.viewId = viewId;
             this.methodName = methodName;
@@ -1068,7 +1073,7 @@
         }
 
         public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(TAG);
+            out.writeInt(SET_REFLECTION_ACTION_WITHOUT_PARAMS_TAG);
             out.writeInt(this.viewId);
             out.writeString(this.methodName);
         }
@@ -1192,7 +1197,7 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(BITMAP_REFLECTION_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeString(methodName);
             dest.writeInt(bitmapId);
@@ -1214,16 +1219,12 @@
         public String getActionName() {
             return "BitmapReflectionAction";
         }
-
-        public final static int TAG = 12;
     }
 
     /**
      * Base class for the reflection actions.
      */
     private final class ReflectionAction extends Action {
-        static final int TAG = 2;
-
         static final int BOOLEAN = 1;
         static final int BYTE = 2;
         static final int SHORT = 3;
@@ -1330,7 +1331,7 @@
         }
 
         public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(TAG);
+            out.writeInt(REFLECTION_ACTION_TAG);
             out.writeInt(this.viewId);
             out.writeString(this.methodName);
             out.writeInt(this.type);
@@ -1555,137 +1556,220 @@
     }
 
     /**
-     * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
-     * given {@link RemoteViews}, or calling {@link ViewGroup#removeAllViews()}
-     * when null. This allows users to build "nested" {@link RemoteViews}.
+     * ViewGroup methods that are related to adding Views.
      */
-    private class ViewGroupAction extends Action {
-        public ViewGroupAction(int viewId, RemoteViews nestedViews) {
+    private class ViewGroupActionAdd extends Action {
+        private RemoteViews mNestedViews;
+        private int mIndex;
+
+        ViewGroupActionAdd(int viewId, RemoteViews nestedViews) {
+            this(viewId, nestedViews, -1 /* index */);
+        }
+
+        ViewGroupActionAdd(int viewId, RemoteViews nestedViews, int index) {
             this.viewId = viewId;
-            this.nestedViews = nestedViews;
+            mNestedViews = nestedViews;
+            mIndex = index;
             if (nestedViews != null) {
                 configureRemoteViewsAsChild(nestedViews);
             }
         }
 
-        ViewGroupAction(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info, int depth) {
+        ViewGroupActionAdd(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info,
+                int depth) {
             viewId = parcel.readInt();
-            boolean nestedViewsNull = parcel.readInt() == 0;
-            if (!nestedViewsNull) {
-                nestedViews = new RemoteViews(parcel, bitmapCache, info, depth);
-            } else {
-                nestedViews = null;
-            }
+            mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(VIEW_GROUP_ACTION_ADD_TAG);
             dest.writeInt(viewId);
-            if (nestedViews != null) {
-                dest.writeInt(1);
-                nestedViews.writeToParcel(dest, flags);
-            } else {
-                // signifies null
-                dest.writeInt(0);
-            }
+            mNestedViews.writeToParcel(dest, flags);
         }
 
         @Override
         public boolean hasSameAppInfo(ApplicationInfo parentInfo) {
-            return nestedViews != null
-                    && nestedViews.mApplication.packageName.equals(parentInfo.packageName)
-                    && nestedViews.mApplication.uid == parentInfo.uid;
+            return mNestedViews.mApplication.packageName.equals(parentInfo.packageName)
+                    && mNestedViews.mApplication.uid == parentInfo.uid;
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
             final Context context = root.getContext();
             final ViewGroup target = root.findViewById(viewId);
-            if (target == null) return;
-            if (nestedViews != null) {
-                // Inflate nested views and add as children
-                target.addView(nestedViews.apply(context, target, handler));
-            } else {
-                // Clear all children when nested views omitted
-                target.removeAllViews();
+
+            if (target == null) {
+                return;
             }
+
+            // Inflate nested views and add as children
+            target.addView(mNestedViews.apply(context, target, handler), mIndex);
         }
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
             // In the async implementation, update the view tree so that subsequent calls to
-            // findViewById return the currect view.
+            // findViewById return the current view.
             root.createTree();
             ViewTree target = root.findViewTreeById(viewId);
             if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
                 return ACTION_NOOP;
             }
             final ViewGroup targetVg = (ViewGroup) target.mRoot;
-            if (nestedViews == null) {
-                // Clear all children when nested views omitted
-                target.mChildren = null;
-                return new RuntimeAction() {
-                    @Override
-                    public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
-                            throws ActionException {
-                        targetVg.removeAllViews();
-                    }
-                };
-            } else {
-                // Inflate nested views and perform all the async tasks for the child remoteView.
-                final Context context = root.mRoot.getContext();
-                final AsyncApplyTask task = nestedViews.getAsyncApplyTask(
-                        context, targetVg, null, handler);
-                final ViewTree tree = task.doInBackground();
-                if (tree == null) {
-                    throw new ActionException(task.mError);
-                }
 
-                // Update the global view tree, so that next call to findViewTreeById
-                // goes through the subtree as well.
-                target.addChild(tree);
+            // Inflate nested views and perform all the async tasks for the child remoteView.
+            final Context context = root.mRoot.getContext();
+            final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(
+                    context, targetVg, null, handler);
+            final ViewTree tree = task.doInBackground();
 
-                return new RuntimeAction() {
-
-                    @Override
-                    public void apply(View root, ViewGroup rootParent, OnClickHandler handler) throws ActionException {
-                        task.onPostExecute(tree);
-                        targetVg.addView(task.mResult);
-                    }
-                };
+            if (tree == null) {
+                throw new ActionException(task.mError);
             }
+
+            // Update the global view tree, so that next call to findViewTreeById
+            // goes through the subtree as well.
+            target.addChild(tree, mIndex);
+
+            return new RuntimeAction() {
+                @Override
+                public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+                        throws ActionException {
+                    task.onPostExecute(tree);
+                    targetVg.addView(task.mResult, mIndex);
+                }
+            };
         }
 
         @Override
         public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
-            if (nestedViews != null) {
-                counter.increment(nestedViews.estimateMemoryUsage());
-            }
+            counter.increment(mNestedViews.estimateMemoryUsage());
         }
 
         @Override
         public void setBitmapCache(BitmapCache bitmapCache) {
-            if (nestedViews != null) {
-                nestedViews.setBitmapCache(bitmapCache);
-            }
+            mNestedViews.setBitmapCache(bitmapCache);
         }
 
-        public String getActionName() {
-            return "ViewGroupAction" + (nestedViews == null ? "Remove" : "Add");
-        }
-
+        @Override
         public int mergeBehavior() {
             return MERGE_APPEND;
         }
 
         @Override
         public boolean prefersAsyncApply() {
-            return nestedViews != null && nestedViews.prefersAsyncApply();
+            return mNestedViews.prefersAsyncApply();
         }
 
-        RemoteViews nestedViews;
 
-        public final static int TAG = 4;
+        @Override
+        public String getActionName() {
+            return "ViewGroupActionAdd";
+        }
+    }
+
+    /**
+     * ViewGroup methods related to removing child views.
+     */
+    private class ViewGroupActionRemove extends Action {
+        /**
+         * Id that indicates that all child views of the affected ViewGroup should be removed.
+         *
+         * <p>Using -2 because the default id is -1. This avoids accidentally matching that.
+         */
+        private static final int REMOVE_ALL_VIEWS_ID = -2;
+
+        private int mViewIdToKeep;
+
+        ViewGroupActionRemove(int viewId) {
+            this(viewId, REMOVE_ALL_VIEWS_ID);
+        }
+
+        ViewGroupActionRemove(int viewId, int viewIdToKeep) {
+            this.viewId = viewId;
+            mViewIdToKeep = viewIdToKeep;
+        }
+
+        ViewGroupActionRemove(Parcel parcel) {
+            viewId = parcel.readInt();
+            mViewIdToKeep = parcel.readInt();
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(VIEW_GROUP_ACTION_REMOVE_TAG);
+            dest.writeInt(viewId);
+            dest.writeInt(mViewIdToKeep);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+            final ViewGroup target = root.findViewById(viewId);
+
+            if (target == null) {
+                return;
+            }
+
+            if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
+                target.removeAllViews();
+                return;
+            }
+
+            removeAllViewsExceptIdToKeep(target);
+        }
+
+        @Override
+        public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
+            // In the async implementation, update the view tree so that subsequent calls to
+            // findViewById return the current view.
+            root.createTree();
+            ViewTree target = root.findViewTreeById(viewId);
+
+            if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
+                return ACTION_NOOP;
+            }
+
+            final ViewGroup targetVg = (ViewGroup) target.mRoot;
+
+            // Clear all children when nested views omitted
+            target.mChildren = null;
+            return new RuntimeAction() {
+                @Override
+                public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+                        throws ActionException {
+                    if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
+                        targetVg.removeAllViews();
+                        return;
+                    }
+
+                    removeAllViewsExceptIdToKeep(targetVg);
+                }
+            };
+        }
+
+        /**
+         * Iterates through the children in the given ViewGroup and removes all the views that
+         * do not have an id of {@link #mViewIdToKeep}.
+         */
+        private void removeAllViewsExceptIdToKeep(ViewGroup viewGroup) {
+            // Otherwise, remove all the views that do not match the id to keep.
+            int index = viewGroup.getChildCount() - 1;
+            while (index >= 0) {
+                if (viewGroup.getChildAt(index).getId() != mViewIdToKeep) {
+                    viewGroup.removeViewAt(index);
+                }
+                index--;
+            }
+        }
+
+        @Override
+        public String getActionName() {
+            return "ViewGroupActionRemove";
+        }
+
+        @Override
+        public int mergeBehavior() {
+            return MERGE_APPEND;
+        }
     }
 
     /**
@@ -1740,7 +1824,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(TEXT_VIEW_DRAWABLE_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeInt(isRelative ? 1 : 0);
             dest.writeInt(useIcons ? 1 : 0);
@@ -1850,8 +1934,6 @@
 
         boolean drawablesLoaded = false;
         Drawable id1, id2, id3, id4;
-
-        public final static int TAG = 11;
     }
 
     /**
@@ -1871,7 +1953,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(TEXT_VIEW_SIZE_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeInt(units);
             dest.writeFloat(size);
@@ -1890,8 +1972,6 @@
 
         int units;
         float size;
-
-        public final static int TAG = 13;
     }
 
     /**
@@ -1915,7 +1995,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(VIEW_PADDING_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeInt(left);
             dest.writeInt(top);
@@ -1935,8 +2015,6 @@
         }
 
         int left, top, right, bottom;
-
-        public final static int TAG = 14;
     }
 
     /**
@@ -1968,7 +2046,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(LAYOUT_PARAM_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeInt(property);
             dest.writeInt(value);
@@ -2021,8 +2099,6 @@
 
         int property;
         int value;
-
-        public final static int TAG = 19;
     }
 
     /**
@@ -2057,7 +2133,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(TEXT_VIEW_DRAWABLE_COLOR_FILTER_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeInt(isRelative ? 1 : 0);
             dest.writeInt(index);
@@ -2090,8 +2166,6 @@
         final int index;
         final int color;
         final PorterDuff.Mode mode;
-
-        public final static int TAG = 17;
     }
 
     /**
@@ -2110,7 +2184,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_REMOTE_INPUTS_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeTypedArray(remoteInputs, flags);
         }
@@ -2128,7 +2202,6 @@
         }
 
         final Parcelable[] remoteInputs;
-        public final static int TAG = 18;
     }
 
     /**
@@ -2271,56 +2344,59 @@
                 for (int i=0; i<count; i++) {
                     int tag = parcel.readInt();
                     switch (tag) {
-                        case SetOnClickPendingIntent.TAG:
+                        case SET_ON_CLICK_PENDING_INTENT_TAG:
                             mActions.add(new SetOnClickPendingIntent(parcel));
                             break;
-                        case SetDrawableParameters.TAG:
+                        case SET_DRAWABLE_PARAMETERS_TAG:
                             mActions.add(new SetDrawableParameters(parcel));
                             break;
-                        case ReflectionAction.TAG:
+                        case REFLECTION_ACTION_TAG:
                             mActions.add(new ReflectionAction(parcel));
                             break;
-                        case ViewGroupAction.TAG:
-                            mActions.add(new ViewGroupAction(parcel, mBitmapCache, mApplication,
+                        case VIEW_GROUP_ACTION_ADD_TAG:
+                            mActions.add(new ViewGroupActionAdd(parcel, mBitmapCache, mApplication,
                                     depth));
                             break;
-                        case ReflectionActionWithoutParams.TAG:
+                        case VIEW_GROUP_ACTION_REMOVE_TAG:
+                            mActions.add(new ViewGroupActionRemove(parcel));
+                            break;
+                        case SET_REFLECTION_ACTION_WITHOUT_PARAMS_TAG:
                             mActions.add(new ReflectionActionWithoutParams(parcel));
                             break;
-                        case SetEmptyView.TAG:
+                        case SET_EMPTY_VIEW_ACTION_TAG:
                             mActions.add(new SetEmptyView(parcel));
                             break;
-                        case SetPendingIntentTemplate.TAG:
+                        case SET_PENDING_INTENT_TEMPLATE_TAG:
                             mActions.add(new SetPendingIntentTemplate(parcel));
                             break;
-                        case SetOnClickFillInIntent.TAG:
+                        case SET_ON_CLICK_FILL_IN_INTENT_TAG:
                             mActions.add(new SetOnClickFillInIntent(parcel));
                             break;
-                        case SetRemoteViewsAdapterIntent.TAG:
+                        case SET_REMOTE_VIEW_ADAPTER_INTENT_TAG:
                             mActions.add(new SetRemoteViewsAdapterIntent(parcel));
                             break;
-                        case TextViewDrawableAction.TAG:
+                        case TEXT_VIEW_DRAWABLE_ACTION_TAG:
                             mActions.add(new TextViewDrawableAction(parcel));
                             break;
-                        case TextViewSizeAction.TAG:
+                        case TEXT_VIEW_SIZE_ACTION_TAG:
                             mActions.add(new TextViewSizeAction(parcel));
                             break;
-                        case ViewPaddingAction.TAG:
+                        case VIEW_PADDING_ACTION_TAG:
                             mActions.add(new ViewPaddingAction(parcel));
                             break;
-                        case BitmapReflectionAction.TAG:
+                        case BITMAP_REFLECTION_ACTION_TAG:
                             mActions.add(new BitmapReflectionAction(parcel));
                             break;
-                        case SetRemoteViewsAdapterList.TAG:
+                        case SET_REMOTE_VIEW_ADAPTER_LIST_TAG:
                             mActions.add(new SetRemoteViewsAdapterList(parcel));
                             break;
-                        case TextViewDrawableColorFilterAction.TAG:
+                        case TEXT_VIEW_DRAWABLE_COLOR_FILTER_ACTION_TAG:
                             mActions.add(new TextViewDrawableColorFilterAction(parcel));
                             break;
-                        case SetRemoteInputsAction.TAG:
+                        case SET_REMOTE_INPUTS_ACTION_TAG:
                             mActions.add(new SetRemoteInputsAction(parcel));
                             break;
-                        case LayoutParamAction.TAG:
+                        case LAYOUT_PARAM_ACTION_TAG:
                             mActions.add(new LayoutParamAction(parcel));
                             break;
                         default:
@@ -2469,7 +2545,23 @@
      * @param nestedView {@link RemoteViews} that describes the child.
      */
     public void addView(int viewId, RemoteViews nestedView) {
-        addAction(new ViewGroupAction(viewId, nestedView));
+        addAction(nestedView == null
+                ? new ViewGroupActionRemove(viewId)
+                : new ViewGroupActionAdd(viewId, nestedView));
+    }
+
+    /**
+     * Equivalent to calling {@link ViewGroup#addView(View, int)} after inflating the
+     * given {@link RemoteViews}.
+     *
+     * @param viewId The id of the parent {@link ViewGroup} to add the child into.
+     * @param nestedView {@link RemoveViews} of the child to add.
+     * @param index The position at which to add the child.
+     *
+     * @hide
+     */
+    public void addView(int viewId, RemoteViews nestedView, int index) {
+        addAction(new ViewGroupActionAdd(viewId, nestedView, index));
     }
 
     /**
@@ -2479,7 +2571,20 @@
      *            children from.
      */
     public void removeAllViews(int viewId) {
-        addAction(new ViewGroupAction(viewId, null));
+        addAction(new ViewGroupActionRemove(viewId));
+    }
+
+    /**
+     * Removes all views in the {@link ViewGroup} specified by the {@code viewId} except for any
+     * child that has the {@code viewIdToKeep} as its id.
+     *
+     * @param viewId The id of the parent {@link ViewGroup} to remove children from.
+     * @param viewIdToKeep The id of a child that should not be removed.
+     *
+     * @hide
+     */
+    public void removeAllViewsExceptId(int viewId, int viewIdToKeep) {
+        addAction(new ViewGroupActionRemove(viewId, viewIdToKeep));
     }
 
     /**
@@ -3652,8 +3757,8 @@
      * and can be searched.
      */
     private static class ViewTree {
+        private static final int INSERT_AT_END_INDEX = -1;
         private View mRoot;
-
         private ArrayList<ViewTree> mChildren;
 
         private ViewTree(View root) {
@@ -3706,11 +3811,26 @@
         }
 
         public void addChild(ViewTree child) {
+            addChild(child, INSERT_AT_END_INDEX);
+        }
+
+        /**
+         * Adds the given {@link ViewTree} as a child at the given index.
+         *
+         * @param index The position at which to add the child or -1 to add last.
+         */
+        public void addChild(ViewTree child, int index) {
             if (mChildren == null) {
                 mChildren = new ArrayList<>();
             }
             child.createTree();
-            mChildren.add(child);
+
+            if (index == INSERT_AT_END_INDEX) {
+                mChildren.add(child);
+                return;
+            }
+
+            mChildren.add(index, child);
         }
 
         private void addViewChild(View v) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2f1f890..de56092 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8257,6 +8257,7 @@
      * Automatically computes and sets the text size.
      */
     private void autoSizeText() {
+        if (getMeasuredWidth() <= 0 || getMeasuredHeight() <= 0) return;
         final int maxWidth = getWidth() - getTotalPaddingLeft() - getTotalPaddingRight();
         final int maxHeight = getHeight() - getExtendedPaddingBottom() - getExtendedPaddingTop();
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 17ef77c..31064ac 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -10049,7 +10049,7 @@
         // Read the CPU data for each UID. This will internally generate a snapshot so next time
         // we read, we get a delta. If we are to distribute the cpu time, then do so. Otherwise
         // we just ignore the data.
-        final long startTimeMs = mClocks.elapsedRealtime();
+        final long startTimeMs = mClocks.uptimeMillis();
         mKernelUidCpuTimeReader.readDelta(!mOnBatteryInternal ? null :
                 new KernelUidCpuTimeReader.Callback() {
                     @Override
@@ -10121,7 +10121,7 @@
             readKernelUidCpuFreqTimesLocked();
         }
 
-        final long elapse = (mClocks.elapsedRealtime() - startTimeMs);
+        final long elapse = (mClocks.uptimeMillis() - startTimeMs);
         if (DEBUG_ENERGY_CPU || (elapse >= 100)) {
             Slog.d(TAG, "Reading cpu stats took " + elapse + " ms");
         }
diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java
index ce89501..66b777e 100644
--- a/core/java/com/android/internal/util/DumpUtils.java
+++ b/core/java/com/android/internal/util/DumpUtils.java
@@ -31,7 +31,7 @@
  */
 public final class DumpUtils {
     private static final String TAG = "DumpUtils";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private DumpUtils() {
     }
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index b8c062e..ee16ab6 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -28,7 +28,7 @@
     boolean getBoolean(in String key, in boolean defaultValue, in int userId);
     long getLong(in String key, in long defaultValue, in int userId);
     String getString(in String key, in String defaultValue, in int userId);
-    void setLockCredential(in String credential, int type, in String savedCredential, int userId);
+    void setLockCredential(in String credential, int type, in String savedCredential, int requestedQuality, int userId);
     void resetKeyStore(int userId);
     VerifyCredentialResponse checkCredential(in String credential, int type, int userId,
             in ICheckCredentialProgressCallback progressCallback);
@@ -49,6 +49,7 @@
     long addEscrowToken(in byte[] token, int userId);
     boolean removeEscrowToken(long handle, int userId);
     boolean isEscrowTokenActive(long handle, int userId);
-    boolean setLockCredentialWithToken(String credential, int type, long tokenHandle, in byte[] token, int userId);
+    boolean setLockCredentialWithToken(String credential, int type, long tokenHandle,
+            in byte[] token, int requestedQuality, int userId);
     void unlockUserWithToken(long tokenHandle, in byte[] token, int userId);
 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 51aef73..d476ea0 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -34,6 +35,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.IStorageManager;
@@ -65,6 +67,8 @@
 
     private static final String TAG = "LockPatternUtils";
     private static final boolean DEBUG = false;
+    private static final boolean FRP_CREDENTIAL_ENABLED =
+            Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.frpcredential.enable", false);
 
     /**
      * The key to identify when the lock pattern enabled flag is being accessed for legacy reasons.
@@ -112,6 +116,11 @@
 
     public static final int CREDENTIAL_TYPE_PASSWORD = 2;
 
+    /**
+     * Special user id for triggering the FRP verification flow.
+     */
+    public static final int USER_FRP = UserHandle.USER_NULL + 1;
+
     @Deprecated
     public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
     public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
@@ -295,24 +304,39 @@
     }
 
     public void reportFailedPasswordAttempt(int userId) {
+        if (userId == USER_FRP && frpCredentialEnabled()) {
+            return;
+        }
         getDevicePolicyManager().reportFailedPasswordAttempt(userId);
         getTrustManager().reportUnlockAttempt(false /* authenticated */, userId);
     }
 
     public void reportSuccessfulPasswordAttempt(int userId) {
+        if (userId == USER_FRP && frpCredentialEnabled()) {
+            return;
+        }
         getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId);
         getTrustManager().reportUnlockAttempt(true /* authenticated */, userId);
     }
 
     public void reportPasswordLockout(int timeoutMs, int userId) {
+        if (userId == USER_FRP && frpCredentialEnabled()) {
+            return;
+        }
         getTrustManager().reportUnlockLockout(timeoutMs, userId);
     }
 
     public int getCurrentFailedPasswordAttempts(int userId) {
+        if (userId == USER_FRP && frpCredentialEnabled()) {
+            return 0;
+        }
         return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId);
     }
 
     public int getMaximumFailedPasswordsForWipe(int userId) {
+        if (userId == USER_FRP && frpCredentialEnabled()) {
+            return 0;
+        }
         return getDevicePolicyManager().getMaximumFailedPasswordsForWipe(
                 null /* componentName */, userId);
     }
@@ -586,7 +610,7 @@
 
         try{
             getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential,
-                    userHandle);
+                    DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle);
         } catch (RemoteException e) {
             // well, we tried...
         }
@@ -651,7 +675,7 @@
 
             setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
             getLockSettings().setLockCredential(patternToString(pattern), CREDENTIAL_TYPE_PATTERN,
-                    savedPattern, userId);
+                    savedPattern, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
 
             // Update the device encryption password.
             if (userId == UserHandle.USER_SYSTEM
@@ -765,10 +789,10 @@
      * password.
      * @param password The password to save
      * @param savedPassword The previously saved lock password, or null if none
-     * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
+     * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
      * @param userHandle The userId of the user to change the password for
      */
-    public void saveLockPassword(String password, String savedPassword, int quality,
+    public void saveLockPassword(String password, String savedPassword, int requestedQuality,
             int userHandle) {
         try {
             if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
@@ -777,9 +801,9 @@
             }
 
             final int computedQuality = PasswordMetrics.computeForPassword(password).quality;
-            setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
+            setLong(PASSWORD_TYPE_KEY, Math.max(requestedQuality, computedQuality), userHandle);
             getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword,
-                    userHandle);
+                    requestedQuality, userHandle);
 
             updateEncryptionPasswordIfNeeded(password, computedQuality, userHandle);
             updatePasswordHistory(password, userHandle);
@@ -1474,12 +1498,13 @@
                 }
 
                 final int computedQuality = PasswordMetrics.computeForPassword(credential).quality;
+                int quality = Math.max(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
+                        computedQuality);
                 if (!getLockSettings().setLockCredentialWithToken(credential, type, tokenHandle,
-                        token, userId)) {
+                        token, quality, userId)) {
                     return false;
                 }
-                setLong(PASSWORD_TYPE_KEY, Math.max(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
-                        computedQuality), userId);
+                setLong(PASSWORD_TYPE_KEY, quality, userId);
 
                 updateEncryptionPasswordIfNeeded(credential, computedQuality, userId);
                 updatePasswordHistory(credential, userId);
@@ -1488,7 +1513,8 @@
                     throw new IllegalArgumentException("password must be emtpy for NONE type");
                 }
                 if (!getLockSettings().setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE,
-                        tokenHandle, token, userId)) {
+                        tokenHandle, token, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
+                        userId)) {
                     return false;
                 }
                 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
@@ -1691,4 +1717,12 @@
     public boolean isSyntheticPasswordEnabled() {
         return getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM) != 0;
     }
+
+    public static boolean userOwnsFrpCredential(UserInfo info) {
+        return info != null && info.isPrimary() && info.isAdmin() && frpCredentialEnabled();
+    }
+
+    public static boolean frpCredentialEnabled() {
+        return FRP_CREDENTIAL_ENABLED;
+    }
 }
diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.java b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
index 48109ca..ad6020c 100644
--- a/core/java/com/android/internal/widget/VerifyCredentialResponse.java
+++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
@@ -18,6 +18,8 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.service.gatekeeper.GateKeeperResponse;
+import android.util.Slog;
 
 /**
  * Response object for a ILockSettings credential verification request.
@@ -32,6 +34,7 @@
     public static final VerifyCredentialResponse OK = new VerifyCredentialResponse();
     public static final VerifyCredentialResponse ERROR
             = new VerifyCredentialResponse(RESPONSE_ERROR, 0, null);
+    private static final String TAG = "VerifyCredentialResponse";
 
     private int mResponseCode;
     private byte[] mPayload;
@@ -123,4 +126,29 @@
     private void setPayload(byte[] payload) {
         mPayload = payload;
     }
+
+    public VerifyCredentialResponse stripPayload() {
+        return new VerifyCredentialResponse(mResponseCode, mTimeout, new byte[0]);
+    }
+
+    public static VerifyCredentialResponse fromGateKeeperResponse(
+            GateKeeperResponse gateKeeperResponse) {
+        VerifyCredentialResponse response;
+        int responseCode = gateKeeperResponse.getResponseCode();
+        if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
+            response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
+        } else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
+            byte[] token = gateKeeperResponse.getPayload();
+            if (token == null) {
+                // something's wrong if there's no payload with a challenge
+                Slog.e(TAG, "verifyChallenge response had no associated payload");
+                response = VerifyCredentialResponse.ERROR;
+            } else {
+                response = new VerifyCredentialResponse(token);
+            }
+        } else {
+            response = VerifyCredentialResponse.ERROR;
+        }
+        return response;
+    }
 }
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index fa2ce77..1adc6dd 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -16,7 +16,7 @@
 
 #define ATRACE_TAG ATRACE_TAG_DALVIK
 #define LOG_TAG "AndroidRuntime"
-//#define LOG_NDEBUG 0
+#define LOG_NDEBUG 1
 
 #include <android_runtime/AndroidRuntime.h>
 #include <binder/IBinder.h>
@@ -599,7 +599,6 @@
 {
     JavaVMInitArgs initArgs;
     char propBuf[PROPERTY_VALUE_MAX];
-    char stackTraceDirBuf[sizeof("-Xstacktracedir:")-1 + PROPERTY_VALUE_MAX];
     char stackTraceFileBuf[sizeof("-Xstacktracefile:")-1 + PROPERTY_VALUE_MAX];
     char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
     char heapstartsizeOptsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
@@ -659,7 +658,7 @@
             checkJni = true;
         }
     }
-    ALOGD("CheckJNI is %s\n", checkJni ? "ON" : "OFF");
+    ALOGV("CheckJNI is %s\n", checkJni ? "ON" : "OFF");
     if (checkJni) {
         /* extended JNI checking */
         addOption("-Xcheck:jni");
@@ -680,7 +679,10 @@
     // If dalvik.vm.stack-trace-dir is set, it enables the "new" stack trace
     // dump scheme and a new file is created for each stack dump. If it isn't set,
     // the old scheme is enabled.
-    if (!parseRuntimeOption("dalvik.vm.stack-trace-dir", stackTraceDirBuf, "-Xstacktracedir:")) {
+    property_get("dalvik.vm.stack-trace-dir", propBuf, "");
+    if (strlen(propBuf) > 0) {
+        addOption("-Xusetombstonedtraces");
+    } else {
         parseRuntimeOption("dalvik.vm.stack-trace-file", stackTraceFileBuf, "-Xstacktracefile:");
     }
 
diff --git a/core/jni/android/graphics/GraphicBuffer.cpp b/core/jni/android/graphics/GraphicBuffer.cpp
index 73e53c6..1017cba 100644
--- a/core/jni/android/graphics/GraphicBuffer.cpp
+++ b/core/jni/android/graphics/GraphicBuffer.cpp
@@ -18,6 +18,7 @@
 
 #include "jni.h"
 #include "JNIHelp.h"
+#include <inttypes.h>
 
 #include "android_os_Parcel.h"
 #include "GraphicBuffer.h"
@@ -27,6 +28,8 @@
 
 #include <binder/Parcel.h>
 
+#include <log/log.h>
+
 #include <ui/GraphicBuffer.h>
 #include <ui/PixelFormat.h>
 
@@ -90,9 +93,15 @@
 class GraphicBufferWrapper {
 public:
     explicit GraphicBufferWrapper(const sp<GraphicBuffer>& buffer): buffer(buffer) {
+        LOG_ALWAYS_FATAL_IF(buffer == nullptr, "creating a null GraphicBuffer");
+    }
+    const sp<GraphicBuffer>& get() const {
+        return buffer;
     }
 
-    sp<GraphicBuffer> buffer;
+private:
+    // make sure this is immutable
+    sp<GraphicBuffer> const buffer;
 };
 
 // ----------------------------------------------------------------------------
@@ -102,6 +111,8 @@
 static jlong android_graphics_GraphicBuffer_wrap(JNIEnv* env, jobject clazz,
         jlong unwrapped) {
     sp<GraphicBuffer> b(reinterpret_cast<GraphicBuffer*>(unwrapped));
+    LOG_ALWAYS_FATAL_IF(b == nullptr,
+            "*** android_graphics_GraphicBuffer_wrap() invalid state, b is null, unwrapped=%#" PRIx64, unwrapped);
     GraphicBufferWrapper* wrapper = new GraphicBufferWrapper(b);
     return reinterpret_cast<jlong>(wrapper);
 }
@@ -159,7 +170,7 @@
         return JNI_FALSE;
     }
 
-    sp<GraphicBuffer> buffer(wrapper->buffer);
+    sp<GraphicBuffer> buffer(wrapper->get());
 
     Rect rect(Rect::EMPTY_RECT);
     if (dirtyRect) {
@@ -217,7 +228,7 @@
     nativeCanvas->setBitmap(SkBitmap());
 
     if (wrapper) {
-        status_t status = wrapper->buffer->unlock();
+        status_t status = wrapper->get()->unlock();
         return status == 0 ? JNI_TRUE : JNI_FALSE;
     }
 
@@ -230,11 +241,12 @@
 
 static void android_graphics_GraphicBuffer_write(JNIEnv* env, jobject clazz,
         jlong wrapperHandle, jobject dest) {
+
     GraphicBufferWrapper* wrapper =
                 reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle);
     Parcel* parcel = parcelForJavaObject(env, dest);
     if (parcel) {
-        parcel->write(*wrapper->buffer);
+        parcel->write(*wrapper->get());
     }
 }
 
@@ -260,7 +272,7 @@
         jlong nativeObject = env->GetLongField(obj, gGraphicBufferClassInfo.mNativeObject);
         GraphicBufferWrapper* wrapper = (GraphicBufferWrapper*) nativeObject;
         if (wrapper != NULL) {
-            sp<GraphicBuffer> buffer(wrapper->buffer);
+            sp<GraphicBuffer> buffer(wrapper->get());
             return buffer;
         }
     }
@@ -271,7 +283,7 @@
     GraphicBufferWrapper* wrapper = new GraphicBufferWrapper(buffer);
     jobject obj = env->NewObject(gGraphicBufferClassInfo.mClass,
             gGraphicBufferClassInfo.mConstructorMethodID, buffer->getWidth(), buffer->getHeight(),
-            buffer->getPixelFormat(), buffer->getUsage(), reinterpret_cast<jlong>(wrapper));
+            buffer->getPixelFormat(), (jint)buffer->getUsage(), reinterpret_cast<jlong>(wrapper));
     return obj;
 }
 
diff --git a/core/jni/android_hardware_Radio.cpp b/core/jni/android_hardware_Radio.cpp
index dd33173..39e615c 100644
--- a/core/jni/android_hardware_Radio.cpp
+++ b/core/jni/android_hardware_Radio.cpp
@@ -15,7 +15,7 @@
 ** limitations under the License.
 */
 
-//#define LOG_NDEBUG 0
+#define LOG_NDEBUG 1
 #define LOG_TAG "Radio-JNI"
 #include <utils/Log.h>
 
@@ -961,7 +961,7 @@
 
     int ret = RegisterMethodsOrDie(env, kRadioModuleClassPathName, gModuleMethods, NELEM(gModuleMethods));
 
-    ALOGI("%s DONE", __FUNCTION__);
+    ALOGV("%s DONE", __FUNCTION__);
 
     return ret;
 }
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index de9445d..dd41c77 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -1029,7 +1029,7 @@
         return;
     }
 
-    dump_backtrace_to_file_timeout(pid, fd, timeoutSecs);
+    dump_backtrace_to_file_timeout(pid, kDebuggerdNativeBacktrace, timeoutSecs, fd);
 
     close(fd);
 }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 8b82314..497f00c 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -166,7 +166,7 @@
             buffer->getWidth(),
             buffer->getHeight(),
             buffer->getPixelFormat(),
-            buffer->getUsage(),
+            (jint)buffer->getUsage(),
             (jlong)buffer.get());
 }
 
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 8b73daf..e212c43 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -310,7 +310,7 @@
     SettingProto lte_service_forced = 265;
     SettingProto ephemeral_cookie_max_size_bytes = 266;
     SettingProto enable_ephemeral_feature = 267;
-    SettingProto uninstalled_ephemeral_app_cache_duration_millis = 268;
+    SettingProto installed_instant_app_min_cache_period = 268;
     SettingProto allow_user_switching_when_system_user_locked = 269;
     SettingProto boot_count = 270;
     SettingProto safe_boot_disallowed = 271;
@@ -331,6 +331,10 @@
     SettingProto network_recommendations_package = 286;
     SettingProto bluetooth_a2dp_supports_optional_codecs_prefix = 287;
     SettingProto bluetooth_a2dp_optional_codecs_enabled_prefix = 288;
+    SettingProto installed_instant_app_max_cache_period = 289;
+    SettingProto uninstalled_instant_app_min_cache_period = 290;
+    SettingProto uninstalled_instant_app_max_cache_period = 291;
+    SettingProto unused_static_shared_lib_min_cache_period = 292;
 }
 
 message SecureSettingsProto {
diff --git a/core/res/res/layout/activity_chooser_view_list_item.xml b/core/res/res/layout/activity_chooser_view_list_item.xml
index 66e400a..4678f76 100644
--- a/core/res/res/layout/activity_chooser_view_list_item.xml
+++ b/core/res/res/layout/activity_chooser_view_list_item.xml
@@ -21,7 +21,7 @@
     android:paddingStart="?attr/listPreferredItemPaddingStart"
     android:paddingEnd="?attr/listPreferredItemPaddingEnd"
     android:minWidth="196dip"
-    android:background="?attr/activatedBackgroundIndicator"
+    android:background="?attr/selectableItemBackground"
     android:orientation="vertical" >
 
     <LinearLayout
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index ccd26fb..353a1a5 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -15,12 +15,12 @@
   ~ limitations under the License
   -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:tag="base"
-    >
+    android:tag="base" >
     <include layout="@layout/notification_template_header" />
     <LinearLayout
         android:id="@+id/notification_main_column"
@@ -31,19 +31,14 @@
         android:layout_marginEnd="@dimen/notification_content_margin_end"
         android:layout_marginTop="@dimen/notification_content_margin_top"
         android:layout_marginBottom="@dimen/notification_content_margin_bottom"
-        android:orientation="vertical"
-        >
+        android:orientation="vertical" >
         <include layout="@layout/notification_template_part_line1" />
         <include layout="@layout/notification_template_text" />
+        <include
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/notification_progress_bar_height"
+            android:layout_marginTop="@dimen/notification_progress_margin_top"
+            layout="@layout/notification_template_progress" />
     </LinearLayout>
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom"
-        android:layout_marginStart="@dimen/notification_content_margin_start"
-        android:layout_marginBottom="15dp"
-        android:layout_marginEnd="@dimen/notification_content_margin_end" >
-        <include layout="@layout/notification_template_progress" />
-    </FrameLayout>
     <include layout="@layout/notification_template_right_icon" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index 8b0b476..6b1049a 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -20,51 +20,42 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
-    android:tag="big"
-    >
+    android:tag="big" >
     <LinearLayout
             android:id="@+id/notification_action_list_margin_target"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginBottom="@dimen/notification_action_list_height"
-            android:orientation="vertical"
-            >
+            android:orientation="vertical" >
         <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_gravity="top"
-            >
+            android:layout_gravity="top" >
             <include layout="@layout/notification_template_header" />
             <LinearLayout
                 android:id="@+id/notification_main_column"
                 android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:layout_gravity="top"
+                android:layout_height="wrap_content"
                 android:layout_marginStart="@dimen/notification_content_margin_start"
                 android:layout_marginEnd="@dimen/notification_content_margin_end"
                 android:layout_marginTop="@dimen/notification_content_margin_top"
                 android:layout_marginBottom="@dimen/notification_content_margin_bottom"
-                android:orientation="vertical"
-                >
+                android:orientation="vertical" >
                 <include layout="@layout/notification_template_part_line1" />
                 <include layout="@layout/notification_template_text" />
+                <include
+                    android:layout_width="match_parent"
+                    android:layout_height="@dimen/notification_progress_bar_height"
+                    android:layout_marginTop="@dimen/notification_progress_margin_top"
+                    layout="@layout/notification_template_progress" />
             </LinearLayout>
-            <FrameLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_gravity="bottom"
-                android:layout_marginStart="@dimen/notification_content_margin_start"
-                android:layout_marginBottom="15dp"
-                android:layout_marginEnd="@dimen/notification_content_margin_end">
-                <include layout="@layout/notification_template_progress" />
-            </FrameLayout>
             <include layout="@layout/notification_template_right_icon" />
         </FrameLayout>
-        <ViewStub android:layout="@layout/notification_material_reply_text"
-                android:id="@+id/notification_material_reply_container"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-        />
+        <ViewStub
+            android:layout="@layout/notification_material_reply_text"
+            android:id="@+id/notification_material_reply_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
     </LinearLayout>
     <include layout="@layout/notification_material_action_list" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_progressbar.xml b/core/res/res/layout/notification_template_progressbar.xml
index 61480b8..98e2c03 100644
--- a/core/res/res/layout/notification_template_progressbar.xml
+++ b/core/res/res/layout/notification_template_progressbar.xml
@@ -18,6 +18,6 @@
 <ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@android:id/progress"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:layout_height="@dimen/notification_progress_bar_height"
     style="@style/Widget.Material.Light.ProgressBar.Horizontal"
     />
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index cdc0af7..5e2334d 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -177,6 +177,12 @@
     <!-- height of the content margin on the bottom -->
     <dimen name="notification_content_margin_bottom">16dp</dimen>
 
+    <!-- The height of the progress bar. -->
+    <dimen name="notification_progress_bar_height">15dp</dimen>
+
+    <!-- The top margin before the notification progress bar. -->
+    <dimen name="notification_progress_margin_top">8dp</dimen>
+
     <!-- height of the notification header (for icon and package name) -->
     <dimen name="notification_header_height">48dp</dimen>
 
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 7517946..8755e37 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -758,6 +758,14 @@
         <item name="colorControlNormal">?attr/textColorPrimary</item>
     </style>
 
+    <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Light.Dialog">
+        <!-- Color palette -->
+        <item name="colorPrimary">@color/primary_device_default_settings_light</item>
+        <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
+        <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
+        <item name="colorAccent">@color/accent_device_default_light</item>
+    </style>
+
     <!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
     <style name="Theme.DeviceDefault.Settings.Dark.NoActionBar" parent="Theme.Material.NoActionBar">
         <!-- Color palette -->
diff --git a/core/tests/coretests/src/android/content/pm/AppCacheTest.java b/core/tests/coretests/src/android/content/pm/AppCacheTest.java
index 15dbddf..59aa50a 100644
--- a/core/tests/coretests/src/android/content/pm/AppCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/AppCacheTest.java
@@ -492,7 +492,7 @@
             PackageDataObserver observer = new PackageDataObserver();
             //wait on observer
             synchronized(observer) {
-                getPm().freeStorageAndNotify(null, idealStorageSize, observer);
+                getPm().freeStorageAndNotify(null, idealStorageSize, 0, observer);
                 long waitTime = 0;
                 while(!observer.isDone() || (waitTime > MAX_WAIT_TIME)) {
                     observer.wait(WAIT_TIME_INCR);
@@ -517,7 +517,7 @@
         try {
             // Spin lock waiting for call back
             synchronized(r) {
-                getPm().freeStorage(null, idealStorageSize, pi.getIntentSender());
+                getPm().freeStorage(null, idealStorageSize, 0, pi.getIntentSender());
                 long waitTime = 0;
                 while(!r.isDone() && (waitTime < MAX_WAIT_TIME)) {
                     r.wait(WAIT_TIME_INCR);
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 02ff209..7dc72db7 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -335,7 +335,11 @@
                     Settings.Global.TRUSTED_SOUND,
                     Settings.Global.TZINFO_UPDATE_CONTENT_URL,
                     Settings.Global.TZINFO_UPDATE_METADATA_URL,
-                    Settings.Global.UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS,
+                    Settings.Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
+                    Settings.Global.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
+                    Settings.Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
+                    Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
+                    Settings.Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
                     Settings.Global.UNLOCK_SOUND,
                     Settings.Global.USE_GOOGLE_MAIL,
                     Settings.Global.VT_IMS_ENABLED,
diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java
index fd57baa..115af5e 100644
--- a/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/procstats/SparseMappingTableTest.java
@@ -180,7 +180,9 @@
 
         try {
             table.getValue(key);
-            throw new Exception("Exception not thrown after mismatched reset calls.");
+            // Turn off this assertion because the check in SparseMappingTable.assertConsistency
+            // is also turned off.
+            //throw new Exception("Exception not thrown after mismatched reset calls.");
         } catch (RuntimeException ex) {
             // Good
         }
diff --git a/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java b/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java
index 906d7e9..a249925 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ImageFloatingTextViewTest.java
@@ -38,6 +38,7 @@
     public void setup() {
         mContext = InstrumentationRegistry.getTargetContext();
         mView = new ImageFloatingTextView(mContext, null, 0, 0);
+        mView.setMaxLines(9);
         mTextView = new TextView(mContext, null, 0, 0);
         mTextView.setMaxLines(9);
     }
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
new file mode 100644
index 0000000..f73950a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.UserHandle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LockPatternUtilsTest {
+
+    @Test
+    public void testUserFrp_isNotRegularUser() throws Exception {
+        assertTrue(LockPatternUtils.USER_FRP < 0);
+    }
+
+    @Test
+    public void testUserFrp_isNotAReservedSpecialUser() throws Exception {
+        assertNotEquals(UserHandle.USER_NULL, LockPatternUtils.USER_FRP);
+        assertNotEquals(UserHandle.USER_ALL, LockPatternUtils.USER_FRP);
+        assertNotEquals(UserHandle.USER_CURRENT, LockPatternUtils.USER_FRP);
+        assertNotEquals(UserHandle.USER_CURRENT_OR_SELF, LockPatternUtils.USER_FRP);
+    }
+}
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 5baa75b..7b2fa76 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -81,6 +81,33 @@
 build-one-font-module :=
 font_src_files :=
 
+################################
+# Copies the font configuration file into system/etc for the product as fonts.xml.
+# In the case where $(ADDITIONAL_FONTS_FILE) is defined, the content of $(ADDITIONAL_FONTS_FILE)
+# is added to the $(AOSP_FONTS_FILE).
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fonts.xml
+LOCAL_MODULE_CLASS := ETC
+
+AOSP_FONTS_FILE := frameworks/base/data/fonts/fonts.xml
+
+ifdef ADDITIONAL_FONTS_FILE
+ADDITIONAL_FONTS_SCRIPT := frameworks/base/tools/fonts/add_additional_fonts.py
+ADD_ADDITIONAL_FONTS := $(local-generated-sources-dir)/fonts.xml
+
+$(ADD_ADDITIONAL_FONTS): PRIVATE_SCRIPT := $(ADDITIONAL_FONTS_SCRIPT)
+$(ADD_ADDITIONAL_FONTS): PRIVATE_ADDITIONAL_FONTS_FILE := $(ADDITIONAL_FONTS_FILE)
+$(ADD_ADDITIONAL_FONTS): $(ADDITIONAL_FONTS_SCRIPT) $(AOSP_FONTS_FILE) $(ADDITIONAL_FONTS_FILE)
+	rm -f $@
+	python $(PRIVATE_SCRIPT) $@ $(PRIVATE_ADDITIONAL_FONTS_FILE)
+else
+ADD_ADDITIONAL_FONTS := $(AOSP_FONTS_FILE)
+endif
+
+LOCAL_PREBUILT_MODULE_FILE := $(ADD_ADDITIONAL_FONTS)
+
+include $(BUILD_PREBUILT)
 
 # Run sanity tests on fonts on checkbuild
 checkbuild: fontchain_lint
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 23c54ae..e884f2f 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -14,9 +14,7 @@
 
 # Warning: this is actually a product definition, to be inherited from
 
-PRODUCT_COPY_FILES := \
-    frameworks/base/data/fonts/fonts.xml:$(TARGET_COPY_OUT_SYSTEM)/etc/fonts.xml
-
 PRODUCT_PACKAGES := \
     DroidSansMono.ttf \
-    AndroidClock.ttf
+    AndroidClock.ttf \
+    fonts.xml
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index e62df8f..e3b4740 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -529,6 +529,7 @@
         setTextLocales(LocaleList.getAdjustedDefault());
         setElegantTextHeight(false);
         mFontFeatureSettings = null;
+        mFontVariationSettings = null;
     }
 
     /**
@@ -565,6 +566,7 @@
         mBidiFlags = paint.mBidiFlags;
         mLocales = paint.mLocales;
         mFontFeatureSettings = paint.mFontFeatureSettings;
+        mFontVariationSettings = paint.mFontVariationSettings;
     }
 
     /** @hide */
@@ -1594,10 +1596,13 @@
             return true;
         }
 
+        // The null typeface is valid and it is equivalent to Typeface.DEFAULT.
+        // To call isSupportedAxes method, use Typeface.DEFAULT instance.
+        Typeface targetTypeface = mTypeface == null ? Typeface.DEFAULT : mTypeface;
         FontVariationAxis[] axes = FontVariationAxis.fromFontVariationSettings(settings);
         final ArrayList<FontVariationAxis> filteredAxes = new ArrayList<FontVariationAxis>();
         for (final FontVariationAxis axis : axes) {
-            if (mTypeface.isSupportedAxes(axis.getOpenTypeTagValue())) {
+            if (targetTypeface.isSupportedAxes(axis.getOpenTypeTagValue())) {
                 filteredAxes.add(axis);
             }
         }
@@ -1605,7 +1610,7 @@
             return false;
         }
         mFontVariationSettings = settings;
-        setTypeface(Typeface.createFromTypefaceWithVariation(mTypeface, filteredAxes));
+        setTypeface(Typeface.createFromTypefaceWithVariation(targetTypeface, filteredAxes));
         return true;
     }
 
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 5a56f53..c4b56c3 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -725,8 +725,8 @@
     }
 
     /** @hide */
-    public static Typeface createFromTypefaceWithVariation(Typeface family,
-            List<FontVariationAxis> axes) {
+    public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family,
+            @NonNull List<FontVariationAxis> axes) {
         final long ni = family == null ? 0 : family.native_instance;
         return new Typeface(nativeCreateFromTypefaceWithVariation(ni, axes));
     }
@@ -1056,7 +1056,7 @@
                 }
             }
         }
-        return Arrays.binarySearch(mSupportedAxes, axis) > 0;
+        return Arrays.binarySearch(mSupportedAxes, axis) >= 0;
     }
 
     private static native long nativeCreateFromTypeface(long native_instance, int style);
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 39f11b8..4ee47af 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -61,8 +61,17 @@
 static void clipOutline(const Outline& outline, SkCanvas* canvas, const SkRect* pendingClip) {
     Rect possibleRect;
     float radius;
-    LOG_ALWAYS_FATAL_IF(!outline.getAsRoundRect(&possibleRect, &radius),
-            "clipping outlines should be at most roundedRects");
+
+    /* To match the existing HWUI behavior we only supports rectangles or
+     * rounded rectangles; passing in a more complicated outline fails silently.
+     */
+    if (!outline.getAsRoundRect(&possibleRect, &radius)) {
+        if (pendingClip) {
+            canvas->clipRect(*pendingClip);
+        }
+        return;
+    }
+
     SkRect rect = possibleRect.toSkRect();
     if (radius != 0.0f) {
         if (pendingClip && !pendingClip->contains(rect)) {
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index e0f5e54..975f849 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -21,7 +21,6 @@
 
 #include <SkBlurMask.h>
 #include <SkBlurMaskFilter.h>
-#include <SkGaussianEdgeShader.h>
 #include <SkPathOps.h>
 #include <SkRRectsGaussianEdgeMaskFilter.h>
 #include <SkShadowUtils.h>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
index 34fdc9d..a8f6f02 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
@@ -63,15 +63,17 @@
         public long audioBytes;
         public long videoBytes;
         public long imageBytes;
+        public long appBytes;
 
         /** Convenience method for testing. */
         @VisibleForTesting
         public ExternalStorageStats(
-                long totalBytes, long audioBytes, long videoBytes, long imageBytes) {
+                long totalBytes, long audioBytes, long videoBytes, long imageBytes, long appBytes) {
             this.totalBytes = totalBytes;
             this.audioBytes = audioBytes;
             this.videoBytes = videoBytes;
             this.imageBytes = imageBytes;
+            this.appBytes = appBytes;
         }
 
         /**
@@ -84,6 +86,7 @@
             audioBytes = stats.getAudioBytes();
             videoBytes = stats.getVideoBytes();
             imageBytes = stats.getImageBytes();
+            appBytes = stats.getAppBytes();
         }
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 9309359..b328933 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -888,8 +888,20 @@
                 Settings.Global.ENABLE_EPHEMERAL_FEATURE,
                 GlobalSettingsProto.ENABLE_EPHEMERAL_FEATURE);
         dumpSetting(s, p,
-                Settings.Global.UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS,
-                GlobalSettingsProto.UNINSTALLED_EPHEMERAL_APP_CACHE_DURATION_MILLIS);
+                Settings.Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
+                GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD);
+        dumpSetting(s, p,
+                Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
+                GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
+        dumpSetting(s, p,
+                Settings.Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
+                GlobalSettingsProto.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD);
+        dumpSetting(s, p,
+                Settings.Global.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
+                GlobalSettingsProto.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
+        dumpSetting(s, p,
+                Settings.Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
+                GlobalSettingsProto.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD);
         dumpSetting(s, p,
                 Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED,
                 GlobalSettingsProto.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED);
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 279cca7..988b986 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -958,17 +958,24 @@
         }
 
         final Intent notifIntent;
+        boolean useChooser = true;
 
         // Send through warning dialog by default
         if (getWarningState(mContext, STATE_UNKNOWN) != STATE_HIDE) {
             notifIntent = buildWarningIntent(mContext, sendIntent);
+            // No need to show a chooser in this case.
+            useChooser = false;
         } else {
             notifIntent = sendIntent;
         }
         notifIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
         // Send the share intent...
-        sendShareIntent(mContext, notifIntent);
+        if (useChooser) {
+            sendShareIntent(mContext, notifIntent);
+        } else {
+            mContext.startActivity(notifIntent);
+        }
 
         // ... and stop watching this process.
         stopProgress(id);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f57f48f..3ee451a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -536,7 +536,7 @@
         <activity
             android:name=".settings.BrightnessDialog"
             android:label="@string/quick_settings_brightness_dialog_title"
-            android:theme="@android:style/Theme.DeviceDefault.Light.Dialog"
+            android:theme="@*android:style/Theme.DeviceDefault.QuickSettings.Dialog"
             android:finishOnCloseSystemDialogs="true"
             android:launchMode="singleInstance"
             android:excludeFromRecents="true"
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml b/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml
deleted file mode 100644
index 195849a..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_1x.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32dp"
-        android:viewportWidth="12.0"
-        android:viewportHeight="12.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M3.500000,11.000000L1.800000,11.000000L1.800000,4.400000L0.200000,5.100000L0.200000,3.700000l3.100000,-1.300000l0.200000,0.000000L3.500000,11.000000z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M8.600000,5.500000l1.200000,-3.000000l1.900000,0.000000L9.700000,6.700000l2.200000,4.300000L9.900000,11.000000L8.700000,7.900000L7.400000,11.000000L5.500000,11.000000l2.100000,-4.300000L5.600000,2.500000l1.900000,0.000000L8.600000,5.500000z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml b/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml
deleted file mode 100644
index 68c4307..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_3g.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32dp"
-        android:height="32dp"
-        android:viewportWidth="13.0"
-        android:viewportHeight="13.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M2.000000,6.000000l0.800000,0.000000c0.300000,0.000000 0.500000,-0.100000 0.700000,-0.300000s0.200000,-0.500000 0.200000,-0.900000c0.000000,-0.300000 -0.100000,-0.600000 -0.200000,-0.800000S3.200000,3.700000 2.900000,3.700000C2.700000,3.700000 2.500000,3.800000 2.300000,4.000000S2.100000,4.400000 2.100000,4.700000L0.500000,4.700000C0.500000,4.000000 0.700000,3.400000 1.100000,3.000000s1.000000,-0.600000 1.700000,-0.600000c0.800000,0.000000 1.400000,0.200000 1.900000,0.600000s0.700000,1.000000 0.700000,1.800000c0.000000,0.400000 -0.100000,0.700000 -0.300000,1.100000S4.600000,6.500000 4.300000,6.600000C4.700000,6.800000 5.000000,7.100000 5.200000,7.400000s0.300000,0.700000 0.300000,1.200000c0.000000,0.800000 -0.200000,1.400000 -0.700000,1.800000s-1.100000,0.700000 -1.900000,0.700000c-0.700000,0.000000 -1.300000,-0.200000 -1.800000,-0.600000s-0.700000,-1.000000 -0.700000,-1.800000L2.000000,8.700000C2.000000,9.000000 2.100000,9.300000 2.300000,9.500000s0.400000,0.300000 0.600000,0.300000c0.300000,0.000000 0.500000,-0.100000 0.700000,-0.300000S3.900000,9.000000 3.900000,8.600000c0.000000,-0.500000 -0.100000,-0.800000 -0.300000,-1.000000S3.200000,7.300000 2.800000,7.300000L2.000000,7.300000L2.000000,6.000000z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M12.500000,9.900000c-0.200000,0.400000 -0.600000,0.700000 -1.000000,0.900000s-1.000000,0.400000 -1.800000,0.400000c-0.900000,0.000000 -1.700000,-0.300000 -2.200000,-0.800000S6.700000,9.000000 6.700000,7.900000L6.700000,5.600000c0.000000,-1.100000 0.300000,-1.900000 0.800000,-2.400000s1.200000,-0.800000 2.100000,-0.800000c1.000000,0.000000 1.700000,0.200000 2.100000,0.700000s0.700000,1.200000 0.700000,2.100000l-1.600000,0.000000c0.000000,-0.500000 -0.100000,-0.900000 -0.200000,-1.100000s-0.500000,-0.300000 -0.900000,-0.300000c-0.400000,0.000000 -0.700000,0.200000 -0.900000,0.500000S8.400000,5.000000 8.400000,5.600000l0.000000,2.300000c0.000000,0.700000 0.100000,1.100000 0.300000,1.400000s0.600000,0.500000 1.000000,0.500000c0.300000,0.000000 0.600000,0.000000 0.700000,-0.100000s0.300000,-0.200000 0.400000,-0.300000L10.799999,7.800000L9.600000,7.800000L9.600000,6.600000l2.900000,0.000000L12.500000,9.900000z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml b/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml
deleted file mode 100644
index 61ecc9c..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_4g.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32dp"
-        android:viewportWidth="12.0"
-        android:viewportHeight="12.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M4.600000,7.800000l0.700000,0.000000l0.000000,1.300000L4.600000,9.100000L4.600000,11.000000L3.000000,11.000000L3.000000,9.200000L0.100000,9.200000L0.000000,8.100000L3.000000,2.500000l1.700000,0.000000L4.700000,7.800000zM1.600000,7.800000L3.000000,7.800000l0.000000,-3.000000L2.900000,5.000000L1.600000,7.800000z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M11.900000,9.900000c-0.200000,0.400000 -0.600000,0.700000 -1.000000,0.900000s-1.000000,0.400000 -1.800000,0.400000c-0.900000,0.000000 -1.700000,-0.300000 -2.200000,-0.800000S6.100000,9.000000 6.100000,7.900000L6.100000,5.600000c0.000000,-1.100000 0.300000,-1.900000 0.800000,-2.400000S8.100000,2.400000 9.000000,2.400000c1.000000,0.000000 1.700000,0.200000 2.100000,0.700000s0.700000,1.200000 0.700000,2.100000l-1.600000,0.000000c0.000000,-0.500000 -0.100000,-0.900000 -0.200000,-1.100000S9.500000,3.700000 9.000000,3.700000c-0.400000,0.000000 -0.700000,0.200000 -0.900000,0.500000S7.700000,5.000000 7.700000,5.600000l0.000000,2.300000c0.000000,0.700000 0.100000,1.100000 0.300000,1.400000s0.600000,0.500000 1.000000,0.500000c0.300000,0.000000 0.600000,0.000000 0.700000,-0.100000s0.300000,-0.200000 0.400000,-0.300000L10.099999,7.800000L9.000000,7.800000L9.000000,6.600000l2.900000,0.000000L11.900000,9.900000z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml b/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
deleted file mode 100644
index 782fbe4..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-    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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="19.0"
-        android:viewportHeight="19.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M4.6,7.8l0.7,0.0l0.0,1.3L4.6,9.1L4.6,11.0L3.0,11.0L3.0,9.2L0.1,9.2L0.0,8.2l3.0,-5.7l1.7,0.0L4.6,7.8L4.6,7.8zM1.7,7.8L3.0,7.8l0.0,-3.0L2.9,5.0L1.7,7.8z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M11.9,9.9c-0.2,0.4 -0.6,0.7 -1.0,0.9s-1.0,0.4 -1.8,0.4c-0.9,0.0 -1.7,-0.3 -2.2,-0.8S6.1,9.0 6.1,7.9L6.1,5.6c0.0,-1.1 0.3,-1.9 0.8,-2.4S8.2,2.4 9.0,2.4c1.0,0.0 1.7,0.2 2.1,0.7s0.7,1.2 0.7,2.1l-1.6,0.0c0.0,-0.5 -0.1,-0.9 -0.2,-1.1S9.5,3.7 9.0,3.7c-0.4,0.0 -0.7,0.2 -0.9,0.5S7.8,5.0 7.8,5.6l0.0,2.3c0.0,0.7 0.1,1.1 0.3,1.4c0.2,0.3 0.6,0.5 1.0,0.5c0.3,0.0 0.6,0.0 0.7,-0.1s0.3,-0.2 0.4,-0.3L10.2,7.8L9.0,7.8L9.0,6.6l2.9,0.0L11.9,9.9z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M17.7,4.4l-1.900001,0.0 0.0,-1.9 -1.2,0.0 0.0,1.9 -1.900001,0.0 0.0,1.2 1.900001,0.0 0.0,1.9 1.2,0.0 0.0,-1.9 1.900001,0.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_signal_disabled.xml
deleted file mode 100644
index dd5843d..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_disabled.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:autoMirrored="true"
-        android:width="32dp"
-        android:height="32dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M21.799999,22.299999l-1.199999,-1.299999 0.000000,0.000000 -9.600000,-10.000000 0.000000,0.000000 -6.400000,-6.700000 -1.300000,1.300000 6.400000,6.700000 -8.700000,8.700000 16.900000,0.000000 2.600000,2.700001z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M21.000000,1.000000l-8.600000,8.600000 8.600000,9.100000z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_e.xml b/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
deleted file mode 100644
index 4232126..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32dp"
-        android:height="32dp"
-        android:viewportWidth="13.0"
-        android:viewportHeight="13.0">
-  <group
-    android:translateX="3.5" >
-    <path
-      android:fillColor="#FFFFFFFF"
-      android:pathData="M4.400000,7.300000L1.700000,7.300000l0.000000,2.400000l3.300000,0.000000L5.000000,11.000000L0.000000,11.000000L0.000000,2.500000l4.900000,0.000000l0.000000,1.300000L1.700000,3.800000l0.000000,2.100000l2.800000,0.000000L4.500000,7.300000z"/>
-  </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_g.xml b/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
deleted file mode 100644
index 0c512d7..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32dp"
-        android:height="32dp"
-        android:viewportWidth="13.0"
-        android:viewportHeight="13.0">
-    <group android:translateX="3.5" >
-      <path
-          android:fillColor="#FFFFFFFF"
-          android:pathData="M6.500000,9.900000c-0.200000,0.400000 -0.600000,0.700000 -1.000000,0.900000s-1.000000,0.400000 -1.800000,0.400000c-0.900000,0.000000 -1.700000,-0.300000 -2.200000,-0.800000S0.700000,9.000000 0.700000,7.900000L0.700000,5.600000c0.000000,-1.100000 0.300000,-1.900000 0.800000,-2.400000s1.200000,-0.800000 2.100000,-0.800000c1.000000,0.000000 1.700000,0.200000 2.100000,0.700000s0.700000,1.200000 0.700000,2.100000L4.700000,5.200000c0.000000,-0.500000 -0.100000,-0.900000 -0.200000,-1.100000S4.000000,3.700000 3.600000,3.700000c-0.400000,0.000000 -0.700000,0.200000 -0.900000,0.500000S2.300000,5.000000 2.300000,5.600000l0.000000,2.300000c0.000000,0.700000 0.100000,1.100000 0.300000,1.400000s0.600000,0.500000 1.000000,0.500000c0.300000,0.000000 0.600000,0.000000 0.700000,-0.100000s0.300000,-0.200000 0.400000,-0.300000L4.700000,7.800000L3.500000,7.800000L3.500000,6.600000l2.900000,0.000000L6.400000,9.900000z"/>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_h.xml b/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
deleted file mode 100644
index b9572b2..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32dp"
-        android:height="32dp"
-        android:viewportWidth="13.0"
-        android:viewportHeight="13.0">
-      <group
-        android:translateX="3.5" >
-        <path
-            android:fillColor="#FFFFFFFF"
-            android:pathData="M6.000000,11.000000L4.400000,11.000000L4.400000,7.500000L1.700000,7.500000L1.700000,11.000000L0.000000,11.000000L0.000000,2.500000l1.700000,0.000000l0.000000,3.700000l2.700000,0.000000L4.400000,2.500000L6.000000,2.500000L6.000000,11.000000z"/>
-      </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml b/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml
deleted file mode 100644
index a381d03e..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_lte.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32dp"
-        android:height="32dp"
-        android:viewportWidth="13.0"
-        android:viewportHeight="13.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M2.000000,9.700000l2.000000,0.000000L4.000000,11.000000L0.300000,11.000000L0.300000,2.500000L2.000000,2.500000L2.000000,9.700000z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M8.300000,3.800000L7.000000,3.800000L7.000000,11.000000L5.300000,11.000000L5.300000,3.800000L4.000000,3.800000L4.000000,2.500000l4.300000,0.000000L8.300000,3.800000z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M12.400000,7.300000l-1.700000,0.000000l0.000000,2.400000l2.100000,0.000000L12.799999,11.000000L9.000000,11.000000L9.000000,2.500000l3.700000,0.000000l0.000000,1.300000l-2.100000,0.000000l0.000000,2.100000l1.700000,0.000000L12.300000,7.300000z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml b/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml
deleted file mode 100644
index 3bed28a..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_signal_lte_plus.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
-    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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M2.0,9.7l2.0,0.0L4.0,11.0L0.4,11.0L0.4,2.5L2.0,2.5L2.0,9.7z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M8.3,3.8L7.0,3.8L7.0,11.0L5.4,11.0L5.4,3.8L4.0,3.8L4.0,2.5l4.3,0.0L8.3,3.8z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M12.4,7.3l-1.7,0.0l0.0,2.4l2.1,0.0L12.799999,11.0L9.0,11.0L9.0,2.5l3.7,0.0l0.0,1.3l-2.1,0.0l0.0,2.1l1.7,0.0L12.4,7.3L12.4,7.3z"/>
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M18.4,4.4l-1.9,0.0 0.0,-1.9 -1.2,0.0 0.0,1.9 -1.900001,0.0 0.0,1.2 1.900001,0.0 0.0,1.9 1.2,0.0 0.0,-1.9 1.9,0.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index f41c494..34e43ce 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -26,10 +26,6 @@
     android:paddingBottom="8dp"
     android:visibility="invisible">
 
-    <com.android.systemui.ResizingSpace
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/qs_detail_margin_top" />
-
     <include
         android:id="@+id/qs_detail_header"
         layout="@layout/qs_detail_header"
diff --git a/packages/SystemUI/res/layout/qs_detail_header.xml b/packages/SystemUI/res/layout/qs_detail_header.xml
index 871ed67..a1f0ee7 100644
--- a/packages/SystemUI/res/layout/qs_detail_header.xml
+++ b/packages/SystemUI/res/layout/qs_detail_header.xml
@@ -20,34 +20,46 @@
     android:layout_height="wrap_content"
     android:paddingLeft="@dimen/qs_detail_header_padding"
     android:paddingTop="@dimen/qs_detail_header_padding"
-    android:paddingBottom="@dimen/qs_detail_header_bottom_padding"
+    android:paddingBottom="@dimen/qs_detail_items_padding_top"
     android:paddingEnd="@dimen/qs_panel_padding"
     android:background="@drawable/btn_borderless_rect"
+    android:orientation="vertical"
     android:gravity="center">
 
-    <TextView
-        android:id="@android:id/title"
-        android:paddingStart="@dimen/qs_detail_header_text_padding"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:textDirection="locale"
-        android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+    <com.android.systemui.ResizingSpace
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/qs_detail_margin_top" />
 
-    <ImageView
-        android:id="@+id/settings"
-        android:layout_width="@dimen/qs_detail_image_width"
-        android:layout_height="@dimen/qs_detail_image_height"
-        android:background="?android:attr/selectableItemBackground"
-        android:padding="@dimen/qs_detail_image_padding"
-        android:src="@drawable/ic_settings"
-        android:visibility="gone"/>
-
-    <Switch
-        android:id="@android:id/toggle"
-        android:layout_width="wrap_content"
+    <LinearLayout
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:clickable="false"
-        android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@android:id/title"
+            android:paddingStart="@dimen/qs_detail_header_text_padding"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textDirection="locale"
+            android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+
+        <ImageView
+            android:id="@+id/settings"
+            android:layout_width="@dimen/qs_detail_image_width"
+            android:layout_height="@dimen/qs_detail_image_height"
+            android:background="?android:attr/selectableItemBackground"
+            android:padding="@dimen/qs_detail_image_padding"
+            android:src="@drawable/ic_settings"
+            android:visibility="gone"/>
+
+        <Switch
+            android:id="@android:id/toggle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:clickable="false"
+            android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+
+    </LinearLayout>
 
 </com.android.keyguard.AlphaOptimizedLinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_detail_items.xml b/packages/SystemUI/res/layout/qs_detail_items.xml
index b9cdf28..60cba67 100644
--- a/packages/SystemUI/res/layout/qs_detail_items.xml
+++ b/packages/SystemUI/res/layout/qs_detail_items.xml
@@ -20,7 +20,6 @@
     xmlns:sysui="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:paddingTop="@dimen/qs_detail_items_padding_top"
     android:paddingStart="@dimen/qs_detail_padding_start"
     android:paddingEnd="16dp">
 
diff --git a/packages/SystemUI/res/layout/qs_footer.xml b/packages/SystemUI/res/layout/qs_footer.xml
index 577be2f..871d1f3 100644
--- a/packages/SystemUI/res/layout/qs_footer.xml
+++ b/packages/SystemUI/res/layout/qs_footer.xml
@@ -27,6 +27,7 @@
     android:clipChildren="false"
     android:clipToPadding="false"
     android:paddingTop="0dp"
+    android:background="#00000000"
     android:gravity="center_vertical"
     android:orientation="horizontal">
 
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 005e955..66c5dd5 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -29,7 +29,6 @@
     android:clickable="false"
     android:clipChildren="false"
     android:clipToPadding="false"
-    android:paddingBottom="48dp"
     android:paddingTop="0dp"
     android:paddingEnd="0dp"
     android:paddingStart="0dp">
@@ -83,10 +82,9 @@
     <com.android.systemui.qs.QuickQSPanel
         android:id="@+id/quick_qs_panel"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="48dp"
         android:layout_alignParentEnd="true"
-        android:layout_marginTop="36dp"
-        android:layout_marginBottom="8dp"
+        android:layout_marginTop="31dp"
         android:layout_alignParentTop="true"
         android:accessibilityTraversalAfter="@+id/date_time_group"
         android:accessibilityTraversalBefore="@id/expand_indicator"
@@ -95,8 +93,7 @@
         android:layout_marginStart="8dp"
         android:layout_marginEnd="8dp"
         android:focusable="true"
-        android:importantForAccessibility="yes"
-        android:paddingTop="0dp"/>
+        android:importantForAccessibility="yes" />
 
     <com.android.systemui.statusbar.AlphaOptimizedImageView
         android:id="@+id/qs_detail_header_progress"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 8db00b0..07a9a60 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -56,7 +56,8 @@
             android:layout_gravity="@integer/notification_panel_layout_gravity"
             android:layout_marginBottom="@dimen/close_handle_underlap" />
 
-        <include layout="@layout/ambient_indication" />
+        <include layout="@layout/ambient_indication"
+            android:id="@+id/ambient_indication_container" />
 
         <ViewStub
             android:id="@+id/keyguard_user_switcher"
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 8707840..200eabf 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -36,7 +36,6 @@
             android:layout_height="wrap_content"
             android:layout_marginStart="16dp"
             android:layout_marginEnd="16dp"
-            android:layout_marginTop="8dp"
             android:layout_marginBottom="8dp" />
 
         <RelativeLayout
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 99c6227..340cd79 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -257,7 +257,6 @@
     <dimen name="qs_panel_padding_bottom">0dp</dimen>
     <dimen name="qs_detail_header_height">56dp</dimen>
     <dimen name="qs_detail_header_padding">0dp</dimen>
-    <dimen name="qs_detail_header_bottom_padding">0dp</dimen>
     <dimen name="qs_detail_image_width">56dp</dimen>
     <dimen name="qs_detail_image_height">56dp</dimen>
     <dimen name="qs_detail_image_padding">16dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index c35fdd5..ddaeb04 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -376,10 +376,7 @@
                 if (!mSendingHoverAccessibilityEvents) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
-                    AccessibilityNodeInfo info =
-                            PipAccessibilityInteractionConnection.obtainRootAccessibilityNodeInfo();
-                    event.setSource(info);
-                    info.recycle();
+                    event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
                     mAccessibilityManager.sendAccessibilityEvent(event);
                     mSendingHoverAccessibilityEvents = true;
                 }
@@ -389,10 +386,7 @@
                 if (mSendingHoverAccessibilityEvents) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
-                    AccessibilityNodeInfo info =
-                            PipAccessibilityInteractionConnection.obtainRootAccessibilityNodeInfo();
-                    event.setSource(info);
-                    info.recycle();
+                    event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
                     mAccessibilityManager.sendAccessibilityEvent(event);
                     mSendingHoverAccessibilityEvents = false;
                 }
@@ -716,9 +710,15 @@
      * Updates the current movement bounds based on whether the menu is currently visible.
      */
     private void updateMovementBounds(int menuState) {
-        mMovementBounds = menuState == MENU_STATE_FULL
+        boolean isMenuExpanded = menuState == MENU_STATE_FULL;
+        mMovementBounds = isMenuExpanded
                 ? mExpandedMovementBounds
                 : mNormalMovementBounds;
+        try {
+            mPinnedStackController.setMinEdgeSize(isMenuExpanded ? mExpandedShortestEdgeSize : 0);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not set minimized state", e);
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 149b5cc..0d74b7a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -58,6 +58,16 @@
         mBackground = findViewById(R.id.qs_background);
         mGutterHeight = getContext().getResources().getDimensionPixelSize(R.dimen.qs_gutter_height);
         mFullElevation = mQSPanel.getElevation();
+
+        setClickable(true);
+        setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+    }
+
+    @Override
+    public boolean performClick() {
+        // Want to receive clicks so missing QQS tiles doesn't cause collapse, but
+        // don't want to do anything with them.
+        return true;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 87b042d..682c56c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -101,6 +101,8 @@
     private boolean mShowEditIcon;
     private TouchAnimator mAnimator;
     private View mDateTimeGroup;
+    private boolean mKeyguardShowing;
+    private TouchAnimator mAlarmAnimator;
 
     public QSFooter(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -163,13 +165,14 @@
         int remaining = (width - numTiles * size) / (numTiles - 1);
         int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
 
-        final Builder builder = new Builder()
+        mAnimator = new Builder()
                 .addFloat(mSettingsContainer, "translationX", -(remaining - defSpace), 0)
                 .addFloat(mSettingsButton, "rotation", -120, 0)
-                .addFloat(mAlarmStatus, "alpha", 0, 1);
+                .build();
         if (mAlarmShowing) {
-            builder.addFloat(mDate, "alpha", 1, 0)
+            mAlarmAnimator = new Builder().addFloat(mDate, "alpha", 1, 0)
                     .addFloat(mDateTimeGroup, "translationX", 0, -mDate.getWidth())
+                    .addFloat(mAlarmStatus, "alpha", 0, 1)
                     .setListener(new ListenerAdapter() {
                         @Override
                         public void onAnimationAtStart() {
@@ -180,13 +183,13 @@
                         public void onAnimationStarted() {
                             mAlarmStatus.setVisibility(View.VISIBLE);
                         }
-                    });
+                    }).build();
         } else {
+            mAlarmAnimator = null;
             mAlarmStatus.setVisibility(View.GONE);
             mDate.setAlpha(1);
             mDateTimeGroup.setTranslationX(0);
         }
-        mAnimator = builder.build();
         setExpansion(mExpansionAmount);
     }
 
@@ -248,6 +251,11 @@
         return animatorBuilder.build();
     }
 
+    public void setKeyguardShowing(boolean keyguardShowing) {
+        mKeyguardShowing = keyguardShowing;
+        setExpansion(mExpansionAmount);
+    }
+
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
@@ -275,6 +283,8 @@
     public void setExpansion(float headerExpansionFraction) {
         mExpansionAmount = headerExpansionFraction;
         if (mAnimator != null) mAnimator.setPosition(headerExpansionFraction);
+        if (mAlarmAnimator != null) mAlarmAnimator.setPosition(
+                mKeyguardShowing ? 0 : headerExpansionFraction);
 
         if (mSettingsAlpha != null) {
             mSettingsAlpha.setPosition(headerExpansionFraction);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 61fd624..c9c3a7f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -222,6 +222,7 @@
             mQSAnimator.setOnKeyguard(keyguardShowing);
         }
 
+        mFooter.setKeyguardShowing(keyguardShowing);
         updateQsState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 8539cb9..00b883a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -60,6 +60,12 @@
         mTileLayout = new HeaderTileLayout(context);
         mTileLayout.setListening(mListening);
         addView((View) mTileLayout, 0 /* Between brightness and footer */);
+        super.setPadding(0, 0, 0, 0);
+    }
+
+    @Override
+    public void setPadding(int left, int top, int right, int bottom) {
+        // Always have no padding.
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index e457d72..abafd64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -23,13 +23,11 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.RelativeLayout;
-import android.widget.TextClock;
 
 import com.android.settingslib.Utils;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.R.id;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.QSDetail.Callback;
 import com.android.systemui.statusbar.SignalClusterView;
@@ -79,7 +77,7 @@
         battery.setForceShowPercent(true);
         // Don't show the Wi-Fi indicator here, because it is shown just below in the tile.
         SignalClusterView signalCluster = findViewById(R.id.signal_cluster);
-        signalCluster.setForceBlockWifi();
+        signalCluster.setQsSignalCluster();
 
         mActivityStarter = Dependency.get(ActivityStarter.class);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index b346d40..95a9132 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -360,13 +360,20 @@
 
         private void updateItems() {
             if (mItems == null) return;
-            if (mSignalCallback.mInfo.enabled) {
-                mItems.setEmptyState(R.drawable.ic_qs_wifi_detail_empty,
-                        R.string.quick_settings_wifi_detail_empty_text);
-            } else {
+
+            // Wi-Fi is off
+            if (!mSignalCallback.mInfo.enabled) {
                 mItems.setEmptyState(R.drawable.ic_qs_wifi_detail_empty,
                         R.string.wifi_is_off);
+                mItems.setItems(null);
+                return;
             }
+
+            // No available access points
+            mItems.setEmptyState(R.drawable.ic_qs_wifi_detail_empty,
+                    R.string.quick_settings_wifi_detail_empty_text);
+
+            // Build the list
             Item[] items = null;
             if (mAccessPoints != null) {
                 items = new Item[mAccessPoints.length];
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 012accd..9e2ec571fa 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -160,7 +160,7 @@
     private boolean mHomeStackResizable;
     private boolean mAdjustedForIme;
     private DividerState mState;
-    private SurfaceFlingerVsyncChoreographer mSfChoreographer;
+    private final SurfaceFlingerVsyncChoreographer mSfChoreographer;
 
     private final Handler mHandler = new Handler() {
         @Override
@@ -250,20 +250,22 @@
     };
 
     public DividerView(Context context) {
-        super(context);
+        this(context, null);
     }
 
     public DividerView(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
+        this(context, attrs, 0);
     }
 
     public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
+        this(context, attrs, defStyleAttr, 0);
     }
 
     public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        mSfChoreographer = new SurfaceFlingerVsyncChoreographer(mHandler, context.getDisplay(),
+                Choreographer.getInstance());
     }
 
     @Override
@@ -313,8 +315,6 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         EventBus.getDefault().register(this);
-        mSfChoreographer = new SurfaceFlingerVsyncChoreographer(mHandler, getDisplay(),
-                Choreographer.getInstance());
     }
 
     @Override
@@ -832,7 +832,10 @@
                 .setDuration(animDuration)
                 .start();
         mAdjustedForIme = adjustedForIme;
-        if (mHomeStackResizable && adjustedForIme) {
+
+        // Only get new position if home stack is resizable, ime is open and not minimized
+        // (including the animation)
+        if (mHomeStackResizable && adjustedForIme && !mIsInMinimizeInteraction) {
             mDividerPositionBeforeMinimized = getCurrentPosition();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index a1160de..4a5f83f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -91,6 +91,7 @@
     }
 
     private LayoutListener mLayoutListener;
+    private boolean mLowPriorityStateUpdated;
     private final NotificationInflater mNotificationInflater;
     private int mIconTransformContentShift;
     private int mIconTransformContentShiftNoIcon;
@@ -1135,6 +1136,15 @@
         }
     }
 
+
+    public void setLowPriorityStateUpdated(boolean lowPriorityStateUpdated) {
+        mLowPriorityStateUpdated = lowPriorityStateUpdated;
+    }
+
+    public boolean hasLowPriorityStateUpdated() {
+        return mLowPriorityStateUpdated;
+    }
+
     public boolean isLowPriority() {
         return mIsLowPriority;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index a6c0cd1..ae6667f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -250,8 +250,7 @@
         }
     }
 
-    @VisibleForTesting
-    float getViewAlpha() {
+    public float getViewAlpha() {
         return mViewAlpha;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 28a858c..ab41485 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -121,6 +121,7 @@
     private boolean mBlockEthernet;
     private boolean mActivityEnabled;
     private boolean mForceBlockWifi;
+    private boolean mQsSignal;
 
     public SignalClusterView(Context context) {
         this(context, null);
@@ -152,9 +153,10 @@
         updateActivityEnabled();
     }
 
-    public void setForceBlockWifi() {
+    public void setQsSignalCluster() {
         mForceBlockWifi = true;
         mBlockWifi = true;
+        mQsSignal = true;
         if (isAttachedToWindow()) {
             // Re-register to get new callbacks.
             mNetworkController.removeCallback(this);
@@ -292,26 +294,30 @@
         mWifiStrengthId = statusIcon.icon;
         mWifiBadgeId = statusIcon.iconOverlay;
         mWifiDescription = statusIcon.contentDescription;
-        mWifiIn = activityIn && mActivityEnabled;
-        mWifiOut = activityOut && mActivityEnabled;
+        mWifiIn = activityIn && mActivityEnabled && mWifiVisible;
+        mWifiOut = activityOut && mActivityEnabled && mWifiVisible;
 
         apply();
     }
 
     @Override
-    public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
+    public void setMobileDataIndicators(IconState icon, IconState qsIcon, int type,
             int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
             String description, boolean isWide, int subId, boolean roaming) {
         PhoneState state = getState(subId);
         if (state == null) {
             return;
         }
-        state.mMobileVisible = statusIcon.visible && !mBlockMobile;
-        state.mMobileStrengthId = statusIcon.icon;
-        state.mMobileTypeId = statusType;
-        state.mMobileDescription = statusIcon.contentDescription;
+        if (mQsSignal) {
+            icon = qsIcon;
+            type = qsType;
+        }
+        state.mMobileVisible = icon.visible && !mBlockMobile;
+        state.mMobileStrengthId = icon.icon;
+        state.mMobileTypeId = type;
+        state.mMobileDescription = icon.contentDescription;
         state.mMobileTypeDescription = typeContentDescription;
-        state.mIsMobileTypeIconWide = statusType != 0 && isWide;
+        state.mIsMobileTypeIconWide = type != 0 && isWide;
         state.mRoaming = roaming;
         state.mActivityIn = activityIn && mActivityEnabled;
         state.mActivityOut = activityOut && mActivityEnabled;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 8dab069..09aff1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -38,6 +38,7 @@
     private boolean mReorderingAllowed;
     private VisibilityLocationProvider mVisibilityLocationProvider;
     private ArraySet<View> mAllowedReorderViews = new ArraySet<>();
+    private ArraySet<View> mLowPriorityReorderingViews = new ArraySet<>();
     private ArraySet<View> mAddedChildren = new ArraySet<>();
     private boolean mPulsing;
 
@@ -115,6 +116,9 @@
         if (mAddedChildren.contains(row)) {
             return true;
         }
+        if (mLowPriorityReorderingViews.contains(row)) {
+            return true;
+        }
         if (mAllowedReorderViews.contains(row)
                 && !mVisibilityLocationProvider.isInVisibleLocation(row)) {
             return true;
@@ -130,6 +134,7 @@
     public void onReorderingFinished() {
         mAllowedReorderViews.clear();
         mAddedChildren.clear();
+        mLowPriorityReorderingViews.clear();
     }
 
     @Override
@@ -141,6 +146,10 @@
         }
     }
 
+    public void onLowPriorityUpdated(NotificationData.Entry entry) {
+        mLowPriorityReorderingViews.add(entry.row);
+    }
+
     /**
      * Notify the visual stability manager that a new view was added and should be allowed to
      * reorder next time.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 8fe3096..3dc6827 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -1672,6 +1672,10 @@
         mKeyguardBottomArea.setImportantForAccessibility(alpha == 0f
                 ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+        View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer();
+        if (ambientIndicationContainer != null) {
+            ambientIndicationContainer.setAlpha(alpha);
+        }
     }
 
     private float getNotificationsTopY() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 4115437..1952a21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Canvas;
+import android.support.annotation.DimenRes;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewStub;
@@ -32,7 +33,6 @@
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -91,14 +91,16 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        reloadWidth(mQsFrame);
-        reloadWidth(mStackScroller);
+        reloadWidth(mQsFrame, R.dimen.qs_panel_width);
+        reloadWidth(mStackScroller, R.dimen.notification_panel_width);
     }
 
-    private void reloadWidth(View view) {
+    /**
+     * Loads the given width resource and sets it on the given View.
+     */
+    private void reloadWidth(View view, @DimenRes int width) {
         LayoutParams params = (LayoutParams) view.getLayoutParams();
-        params.width = getContext().getResources().getDimensionPixelSize(
-                R.dimen.qs_panel_width);
+        params.width = getResources().getDimensionPixelSize(width);
         view.setLayoutParams(params);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 4b1d7d7..9cb4488 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -28,6 +28,7 @@
 import android.util.Log;
 import android.view.InputDevice;
 import android.view.MotionEvent;
+import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewTreeObserver;
 import android.view.animation.Interpolator;
@@ -1071,21 +1072,25 @@
         });
         animator.start();
         mHeightAnimator = animator;
-        mKeyguardBottomArea.getIndicationArea().animate()
-                .translationY(-mHintDistance)
-                .setDuration(250)
-                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                .withEndAction(new Runnable() {
-                    @Override
-                    public void run() {
-                        mKeyguardBottomArea.getIndicationArea().animate()
-                                .translationY(0)
-                                .setDuration(450)
-                                .setInterpolator(mBounceInterpolator)
-                                .start();
-                    }
-                })
-                .start();
+
+        View[] viewsToAnimate = {
+                mKeyguardBottomArea.getIndicationArea(),
+                mStatusBar.getAmbientIndicationContainer()};
+        for (View v : viewsToAnimate) {
+            if (v == null) {
+                continue;
+            }
+            v.animate()
+                    .translationY(-mHintDistance)
+                    .setDuration(250)
+                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                    .withEndAction(() -> v.animate()
+                            .translationY(0)
+                            .setDuration(450)
+                            .setInterpolator(mBounceInterpolator)
+                            .start())
+                    .start();
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 01f740f..69f5135 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -25,9 +25,8 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.support.v4.graphics.ColorUtils;
 import android.util.Log;
-import android.util.TypedValue;
+import android.util.MathUtils;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
@@ -57,8 +56,12 @@
             = new PathInterpolator(0f, 0, 0.7f, 1f);
     public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED
             = new PathInterpolator(0.3f, 0f, 0.8f, 1f);
-    public static final float GRADIENT_SCRIM_ALPHA = 0.75f;
-    protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.45f;
+    // Default alpha value for most scrims, if unsure use this constant
+    public static final float GRADIENT_SCRIM_ALPHA = 0.60f;
+    // A scrim varies its opacity based on a busyness factor, for example
+    // how many notifications are currently visible.
+    public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.90f;
+    protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = GRADIENT_SCRIM_ALPHA;
     protected static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f;
     private static final float SCRIM_IN_FRONT_ALPHA = GRADIENT_SCRIM_ALPHA;
     private static final float SCRIM_IN_FRONT_ALPHA_LOCKED = GRADIENT_SCRIM_ALPHA;
@@ -256,6 +259,19 @@
         return mDozeInFrontAlpha;
     }
 
+    public void setNotificationCount(int notificationCount) {
+        final float maxNotificationDensity = 3;
+        float notificationDensity = Math.min(notificationCount / maxNotificationDensity, 1f);
+        float newAlpha = MathUtils.map(0, 1,
+                GRADIENT_SCRIM_ALPHA, GRADIENT_SCRIM_ALPHA_BUSY,
+                notificationDensity);
+        if (mScrimBehindAlphaKeyguard != newAlpha) {
+            mScrimBehindAlphaKeyguard = newAlpha;
+            mAnimateChange = true;
+            scheduleUpdate();
+        }
+    }
+
     private float getScrimInFrontAlpha() {
         return mKeyguardUpdateMonitor.needsSlowUnlockTransition()
                 ? SCRIM_IN_FRONT_ALPHA_LOCKED
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 29a881c..ae8f46f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -36,6 +36,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManager.StackId;
 import android.app.ActivityOptions;
@@ -730,6 +731,7 @@
     private boolean mReinflateNotificationsOnUserSwitched;
     private HashMap<String, Entry> mPendingNotifications = new HashMap<>();
     private boolean mClearAllEnabled;
+    @Nullable private View mAmbientIndicationContainer;
 
     private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
         final int N = array.size();
@@ -771,6 +773,7 @@
         mBatteryController = Dependency.get(BatteryController.class);
         mAssistManager = Dependency.get(AssistManager.class);
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+        mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
 
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mDisplay = mWindowManager.getDefaultDisplay();
@@ -1080,6 +1083,9 @@
                 mKeyguardBottomArea.getLockIcon());
         mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
 
+        mAmbientIndicationContainer = mStatusBarWindow.findViewById(
+                R.id.ambient_indication_container);
+
         // set the initial view visibility
         setAreThereNotifications();
 
@@ -1628,9 +1634,14 @@
         mPendingNotifications.remove(entry.key);
         // If there was an async task started after the removal, we don't want to add it back to
         // the list, otherwise we might get leaks.
-        if (mNotificationData.get(entry.key) == null && !entry.row.isRemoved()) {
+        boolean isNew = mNotificationData.get(entry.key) == null;
+        if (isNew && !entry.row.isRemoved()) {
             addEntry(entry);
+        } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
+            mVisualStabilityManager.onLowPriorityUpdated(entry);
+            updateNotificationShade();
         }
+        entry.row.setLowPriorityStateUpdated(false);
     }
 
     private boolean shouldSuppressFullScreenIntent(String key) {
@@ -2779,6 +2790,11 @@
         return mKeyguardIndicationController;
     }
 
+    @Nullable
+    public View getAmbientIndicationContainer() {
+        return mAmbientIndicationContainer;
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
@@ -4387,6 +4403,9 @@
                 mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked);
             }
             mStatusBarView.removePendingHideExpandedRunnables();
+            if (mAmbientIndicationContainer != null) {
+                mAmbientIndicationContainer.setVisibility(View.VISIBLE);
+            }
         } else {
             mKeyguardIndicationController.setVisible(false);
             if (mKeyguardUserSwitcher != null) {
@@ -4395,6 +4414,9 @@
                         mState == StatusBarState.SHADE_LOCKED ||
                         fromShadeLocked);
             }
+            if (mAmbientIndicationContainer != null) {
+                mAmbientIndicationContainer.setVisibility(View.INVISIBLE);
+            }
         }
         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
             mScrimController.setKeyguardShowing(true);
@@ -5253,6 +5275,7 @@
     protected KeyguardManager mKeyguardManager;
     private LockPatternUtils mLockPatternUtils;
     private DeviceProvisionedController mDeviceProvisionedController;
+    protected SystemServicesProxy mSystemServicesProxy;
 
     // UI-specific methods
 
@@ -6314,7 +6337,10 @@
             StatusBarNotification sbn, ExpandableNotificationRow row) {
         row.setNeedsRedaction(needsRedaction(entry));
         boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
+        boolean isUpdate = mNotificationData.get(entry.key) != null;
+        boolean wasLowPriority = row.isLowPriority();
         row.setIsLowPriority(isLowPriority);
+        row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
         // bind the click event to the content area
         mNotificationClicker.register(row, sbn);
 
@@ -6779,6 +6805,9 @@
         // another "changeViewPosition" call is ever added.
         mStackScroller.changeViewPosition(mNotificationShelf,
                 mStackScroller.getChildCount() - offsetFromEnd);
+
+        // Scrim opacity varies based on notification count
+        mScrimController.setNotificationCount(mStackScroller.getNotGoneChildCount());
     }
 
     public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
@@ -6897,6 +6926,7 @@
 
     protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
         if (!mUseHeadsUp || isDeviceInVrMode()) {
+            if (DEBUG) Log.d(TAG, "No peeking: no huns or vr mode");
             return false;
         }
 
@@ -6905,8 +6935,7 @@
             return false;
         }
 
-        boolean inUse = mPowerManager.isScreenOn()
-                && !SystemServicesProxy.getInstance(mContext).isDreaming();
+        boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming();
 
         if (!inUse && !isDozing()) {
             if (DEBUG) {
@@ -6949,6 +6978,12 @@
             }
         }
 
+        // Don't peek notifications that are suppressed due to group alert behavior
+        if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
+            if (DEBUG) Log.d(TAG, "No peeking: suppressed due to group alert behavior");
+            return false;
+        }
+
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
index 6128749..40e3806 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
@@ -33,6 +33,11 @@
         Context getContext();
         void destroy();
         void addCallback(Consumer<T> callback);
+        /**
+         * Triggers the extension to cycle through each of the sources again because something
+         * (like configuration) may have changed.
+         */
+        T reload();
     }
 
     interface ExtensionBuilder<T> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
index 91b6160..c2618cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -140,6 +140,12 @@
             }
         }
 
+        @Override
+        public T reload() {
+            notifyChanged();
+            return get();
+        }
+
         private void notifyChanged() {
             mItem = null;
             for (int i = 0; i < mProducers.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
index 8e51ddb..cc7943b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
@@ -194,6 +194,11 @@
         return true;
     }
 
+    @Override
+    public boolean hasFocusStateSpecified() {
+        return true;
+    }
+
     public void setPressed(boolean pressed) {
         if (mDark != mLastDark && pressed) {
             mRipplePaint = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 67b5596..03a50a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -253,11 +253,6 @@
     }
 
     @Override
-    public int getQsCurrentIconId() {
-        return getCurrentIconId();
-    }
-
-    @Override
     public void notifyListeners(SignalCallback callback) {
         MobileIconGroup icons = getIcons();
 
@@ -276,9 +271,9 @@
         String description = null;
         // Only send data sim callbacks to QS.
         if (mCurrentState.dataSim) {
-            qsTypeIcon = showDataIcon ? icons.mQsDataType : 0;
+            qsTypeIcon = showDataIcon ? icons.mDataType : 0;
             qsIcon = new IconState(mCurrentState.enabled
-                    && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
+                    && !mCurrentState.isEmergency, getCurrentIconId(), contentDescription);
             description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
         }
         boolean activityIn = mCurrentState.dataConnected
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index aaa0568..d91ae39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -22,16 +22,6 @@
 class TelephonyIcons {
     //***** Data connection icons
 
-    static final int QS_DATA_G = R.drawable.ic_qs_signal_g;
-    static final int QS_DATA_3G = R.drawable.ic_qs_signal_3g;
-    static final int QS_DATA_E = R.drawable.ic_qs_signal_e;
-    static final int QS_DATA_H = R.drawable.ic_qs_signal_h;
-    static final int QS_DATA_1X = R.drawable.ic_qs_signal_1x;
-    static final int QS_DATA_4G = R.drawable.ic_qs_signal_4g;
-    static final int QS_DATA_4G_PLUS = R.drawable.ic_qs_signal_4g_plus;
-    static final int QS_DATA_LTE = R.drawable.ic_qs_signal_lte;
-    static final int QS_DATA_LTE_PLUS = R.drawable.ic_qs_signal_lte_plus;
-
     static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_airplane_mode;
 
     static final int ICON_LTE = R.drawable.stat_sys_data_fully_connected_lte;
@@ -46,8 +36,6 @@
 
     static final int ICON_DATA_DISABLED = R.drawable.stat_sys_data_disabled;
 
-    static final int QS_ICON_DATA_DISABLED = R.drawable.ic_qs_data_disabled;
-
     static final MobileIconGroup CARRIER_NETWORK_CHANGE = new MobileIconGroup(
             "CARRIER_NETWORK_CHANGE",
             null,
@@ -75,7 +63,7 @@
             R.string.accessibility_data_connection_3g,
             TelephonyIcons.ICON_3G,
             true,
-            TelephonyIcons.QS_DATA_3G
+            TelephonyIcons.ICON_3G
             );
 
     static final MobileIconGroup WFC = new MobileIconGroup(
@@ -114,7 +102,7 @@
             R.string.accessibility_data_connection_edge,
             TelephonyIcons.ICON_E,
             false,
-            TelephonyIcons.QS_DATA_E
+            TelephonyIcons.ICON_E
             );
 
     static final MobileIconGroup ONE_X = new MobileIconGroup(
@@ -129,7 +117,7 @@
             R.string.accessibility_data_connection_cdma,
             TelephonyIcons.ICON_1X,
             true,
-            TelephonyIcons.QS_DATA_1X
+            TelephonyIcons.ICON_1X
             );
 
     static final MobileIconGroup G = new MobileIconGroup(
@@ -144,7 +132,7 @@
             R.string.accessibility_data_connection_gprs,
             TelephonyIcons.ICON_G,
             false,
-            TelephonyIcons.QS_DATA_G
+            TelephonyIcons.ICON_G
             );
 
     static final MobileIconGroup H = new MobileIconGroup(
@@ -159,7 +147,7 @@
             R.string.accessibility_data_connection_3_5g,
             TelephonyIcons.ICON_H,
             false,
-            TelephonyIcons.QS_DATA_H
+            TelephonyIcons.ICON_H
             );
 
     static final MobileIconGroup FOUR_G = new MobileIconGroup(
@@ -174,7 +162,7 @@
             R.string.accessibility_data_connection_4g,
             TelephonyIcons.ICON_4G,
             true,
-            TelephonyIcons.QS_DATA_4G
+            TelephonyIcons.ICON_4G
             );
 
     static final MobileIconGroup FOUR_G_PLUS = new MobileIconGroup(
@@ -189,7 +177,7 @@
             R.string.accessibility_data_connection_4g_plus,
             TelephonyIcons.ICON_4G_PLUS,
             true,
-            TelephonyIcons.QS_DATA_4G_PLUS
+            TelephonyIcons.ICON_4G_PLUS
             );
 
     static final MobileIconGroup LTE = new MobileIconGroup(
@@ -204,7 +192,7 @@
             R.string.accessibility_data_connection_lte,
             TelephonyIcons.ICON_LTE,
             true,
-            TelephonyIcons.QS_DATA_LTE
+            TelephonyIcons.ICON_LTE
             );
 
     static final MobileIconGroup LTE_PLUS = new MobileIconGroup(
@@ -219,7 +207,7 @@
             R.string.accessibility_data_connection_lte_plus,
             TelephonyIcons.ICON_LTE_PLUS,
             true,
-            TelephonyIcons.QS_DATA_LTE_PLUS
+            TelephonyIcons.ICON_LTE_PLUS
             );
 
     static final MobileIconGroup DATA_DISABLED = new MobileIconGroup(
@@ -234,7 +222,7 @@
             R.string.accessibility_cell_data_off,
             TelephonyIcons.ICON_DATA_DISABLED,
             false,
-            TelephonyIcons.QS_ICON_DATA_DISABLED
+            TelephonyIcons.ICON_DATA_DISABLED
             );
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index e80d3b3..0fd2445 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -36,11 +36,13 @@
 import android.service.notification.IConditionListener;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ZenRule;
+import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 import android.util.Slog;
 
 import com.android.systemui.qs.GlobalSetting;
 import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.util.Utils;
 
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
@@ -169,45 +171,32 @@
     }
 
     private void fireNextAlarmChanged() {
-        for (Callback cb : mCallbacks) {
-            cb.onNextAlarmChanged();
-        }
+        Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged());
     }
 
     private void fireEffectsSuppressorChanged() {
-        for (Callback cb : mCallbacks) {
-            cb.onEffectsSupressorChanged();
-        }
+        Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged());
     }
 
     private void fireZenChanged(int zen) {
-        for (Callback cb : mCallbacks) {
-            cb.onZenChanged(zen);
-        }
+        Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen));
     }
 
     private void fireZenAvailableChanged(boolean available) {
-        for (Callback cb : mCallbacks) {
-            cb.onZenAvailableChanged(available);
-        }
+        Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available));
     }
 
     private void fireConditionsChanged(Condition[] conditions) {
-        for (Callback cb : mCallbacks) {
-            cb.onConditionsChanged(conditions);
-        }
+        Utils.safeForeach(mCallbacks, c -> c.onConditionsChanged(conditions));
     }
 
     private void fireManualRuleChanged(ZenRule rule) {
-        for (Callback cb : mCallbacks) {
-            cb.onManualRuleChanged(rule);
-        }
+        Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule));
     }
 
-    private void fireConfigChanged(ZenModeConfig config) {
-        for (Callback cb : mCallbacks) {
-            cb.onConfigChanged(config);
-        }
+    @VisibleForTesting
+    protected void fireConfigChanged(ZenModeConfig config) {
+        Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config));
     }
 
     private void updateConditions(Condition[] conditions) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 2081561..2d7d17b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -316,7 +316,7 @@
         mNotificationHeaderWrapper.notifyContentUpdated(mContainingNotification);
         recreateLowPriorityHeader(builder);
         recreateAmbientHeader(builder);
-        resetHeaderVisibilityIfNeeded(mNotificationHeader, calculateDesiredHeader());
+        updateHeaderVisibility(false /* animate */);
         updateChildrenHeaderAppearance();
     }
 
@@ -854,6 +854,11 @@
         return mNotificationHeaderLowPriority;
     }
 
+    @VisibleForTesting
+    public ViewGroup getCurrentHeaderView() {
+        return mCurrentHeader;
+    }
+
     public void notifyShowAmbientChanged() {
         updateHeaderVisibility(false);
     }
@@ -890,7 +895,12 @@
                 desiredHeader.setVisibility(VISIBLE);
             }
             if (currentHeader != null) {
-                getWrapperForView(currentHeader).setVisible(false);
+                // Wrapper can be null if we were a low priority notification
+                // and just destroyed it by calling setIsLowPriority(false)
+                NotificationViewWrapper wrapper = getWrapperForView(currentHeader);
+                if (wrapper != null) {
+                    wrapper.setVisible(false);
+                }
                 currentHeader.setVisibility(INVISIBLE);
             }
         }
@@ -899,7 +909,7 @@
         resetHeaderVisibilityIfNeeded(mNotificationHeaderAmbient, desiredHeader);
         resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, desiredHeader);
 
-        mCurrentHeader = currentHeader;
+        mCurrentHeader = desiredHeader;
     }
 
     private void resetHeaderVisibilityIfNeeded(View header, View desiredHeader) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
new file mode 100644
index 0000000..f4aebae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+public class Utils {
+
+    /**
+     * Allows lambda iteration over a list. It is done in reverse order so it is safe
+     * to add or remove items during the iteration.
+     */
+    public static <T> void safeForeach(List<T> list, Consumer<T> c) {
+        for (int i = list.size() - 1; i >= 0; i--) {
+            c.accept(list.get(i));
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 2f9bcff..e86a34a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -18,26 +18,25 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.media.AudioManager;
 import android.media.VolumePolicy;
 import android.os.Bundle;
 import android.os.Handler;
-import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 
+import com.android.settingslib.applications.InterestingConfigChanges;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.Dependency;
 import com.android.systemui.SystemUI;
 import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.plugins.PluginDependency;
 import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.plugins.VolumeDialog;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ExtensionController.Extension;
 import com.android.systemui.tuner.TunerService;
 
 import java.io.FileDescriptor;
@@ -60,6 +59,9 @@
     private final SystemUI mSysui;
     private final Context mContext;
     private final VolumeDialogControllerImpl mController;
+    private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
+            ActivityInfo.CONFIG_FONT_SCALE);
+    private final Extension mExtension;
     private VolumeDialog mDialog;
     private VolumePolicy mVolumePolicy = new VolumePolicy(
             DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT,  // volumeDownToEnterSilent
@@ -76,7 +78,7 @@
         // Allow plugins to reference the VolumeDialogController.
         Dependency.get(PluginDependencyProvider.class)
                 .allowPluginDependency(VolumeDialogController.class);
-        Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
+        mExtension = Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
                 .withPlugin(VolumeDialog.class)
                 .withDefault(this::createDefault)
                 .withCallback(dialog -> {
@@ -148,7 +150,9 @@
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
-        // noop
+        if (mConfigChanges.applyNewConfig(mContext.getResources())) {
+            mExtension.reload();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index fffcc08..a200234 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -57,7 +57,6 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.R.string;
 import com.android.systemui.statusbar.policy.ZenModeController;
 
 import java.io.FileDescriptor;
@@ -292,11 +291,6 @@
     private void onAttach() {
         setExpanded(true);
         mAttached = true;
-        for (int i = 0; i < mZenRadioGroupContent.getChildCount(); i++) {
-            ConditionTag tag = getConditionTagAt(i);
-            if (tag != null) tag.rb.setChecked(false);
-            mZenRadioGroupContent.getChildAt(i).setTag(null);
-        }
         mAttachedZen = mController.getZen();
         ZenRule manualRule = mController.getManualRule();
         mExitCondition = manualRule != null ? manualRule.condition : null;
@@ -309,6 +303,7 @@
         setSessionExitCondition(copy(mExitCondition));
         updateWidgets();
         setRequestingConditions(!mHidden);
+        ensureSelection();
     }
 
     private void onDetach() {
@@ -364,9 +359,6 @@
         if (expanded == mExpanded) return;
         if (DEBUG) Log.d(mTag, "setExpanded " + expanded);
         mExpanded = expanded;
-        if (mExpanded) {
-            ensureSelection();
-        }
         updateWidgets();
         fireExpanded();
     }
@@ -462,7 +454,8 @@
                     ActivityManager.getCurrentUser(), false);
             return c;
         }
-        return null;
+        // If there is a manual rule, but it has no condition listed then it is forever.
+        return forever();
     }
 
     private void handleUpdateZen(int zen) {
@@ -489,6 +482,7 @@
             final ConditionTag tag = getConditionTagAt(i);
             if (tag != null && sameConditionId(tag.condition, mExitCondition)) {
                 bind(exitCondition, mZenRadioGroupContent.getChildAt(i), i);
+                tag.rb.setChecked(true);
                 return;
             }
         }
@@ -496,6 +490,7 @@
                 exitCondition.id)) {
             bind(exitCondition, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
                     COUNTDOWN_CONDITION_INDEX);
+            getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index bf6b394..55ec307 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -16,13 +16,27 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.app.Notification;
 import android.metrics.LogMaker;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IPowerManager;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
 import android.support.test.metricshelper.MetricsAsserts;
 import android.support.test.runner.AndroidJUnit4;
@@ -33,9 +47,11 @@
 import com.android.internal.logging.testing.FakeMetricsLogger;
 import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.statusbar.ActivatableNotificationView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import org.junit.Before;
@@ -52,19 +68,34 @@
     NotificationStackScrollLayout mStackScroller;
     StatusBar mStatusBar;
     FakeMetricsLogger mMetricsLogger;
+    HeadsUpManager mHeadsUpManager;
+    NotificationData mNotificationData;
+    PowerManager mPowerManager;
+    SystemServicesProxy mSystemServicesProxy;
 
     private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
 
     @Before
-    public void setup() {
+    public void setup() throws Exception {
         mStatusBarKeyguardViewManager = mock(StatusBarKeyguardViewManager.class);
         mUnlockMethodCache = mock(UnlockMethodCache.class);
         mKeyguardIndicationController = mock(KeyguardIndicationController.class);
         mStackScroller = mock(NotificationStackScrollLayout.class);
         mMetricsLogger = new FakeMetricsLogger();
+        mHeadsUpManager = mock(HeadsUpManager.class);
+        mNotificationData = mock(NotificationData.class);
+        mSystemServicesProxy = mock(SystemServicesProxy.class);
+        IPowerManager powerManagerService = mock(IPowerManager.class);
+        HandlerThread handlerThread = new HandlerThread("TestThread");
+        handlerThread.start();
+        mPowerManager = new PowerManager(mContext, powerManagerService,
+                new Handler(handlerThread.getLooper()));
+        when(powerManagerService.isInteractive()).thenReturn(true);
+
         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
         mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
-                mKeyguardIndicationController, mStackScroller);
+                mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
+                mNotificationData, mPowerManager, mSystemServicesProxy);
 
         doAnswer(invocation -> {
             OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0];
@@ -210,14 +241,62 @@
                         .setType(MetricsEvent.TYPE_ACTION));
     }
 
+    @Test
+    public void testShouldPeek_nonSuppressedGroupSummary() {
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
+        when(mNotificationData.shouldSuppressScreenOn(any())).thenReturn(false);
+        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+
+        Notification n = new Notification.Builder(getContext(), "a")
+                .setGroup("a")
+                .setGroupSummary(true)
+                .setGroupAlertBehavior(Notification.GROUP_ALERT_SUMMARY)
+                .build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+
+        assertTrue(mStatusBar.shouldPeek(entry, sbn));
+    }
+
+    @Test
+    public void testShouldPeek_suppressedGroupSummary() {
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
+        when(mNotificationData.shouldSuppressScreenOn(any())).thenReturn(false);
+        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+
+        Notification n = new Notification.Builder(getContext(), "a")
+                .setGroup("a")
+                .setGroupSummary(true)
+                .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
+                .build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+
+        assertFalse(mStatusBar.shouldPeek(entry, sbn));
+    }
+
     static class TestableStatusBar extends StatusBar {
         public TestableStatusBar(StatusBarKeyguardViewManager man,
                 UnlockMethodCache unlock, KeyguardIndicationController key,
-                NotificationStackScrollLayout stack) {
+                NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd,
+                PowerManager pm, SystemServicesProxy ssp) {
             mStatusBarKeyguardViewManager = man;
             mUnlockMethodCache = unlock;
             mKeyguardIndicationController = key;
             mStackScroller = stack;
+            mHeadsUpManager = hum;
+            mNotificationData = nd;
+            mUseHeadsUp = true;
+            mPowerManager = pm;
+            mSystemServicesProxy = ssp;
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index cb20639..6157d44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -111,7 +111,7 @@
         String typeDescription = "Test 1";
         String description = "Test 2";
         int type = R.drawable.stat_sys_data_fully_connected_1x;
-        int qsType = R.drawable.ic_qs_signal_1x;
+        int qsType = type;
         boolean wide = true;
         int subId = 5;
         boolean roaming = true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 505e1d8..c233fea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -70,7 +70,7 @@
     protected static final int DEFAULT_SIGNAL_STRENGTH = DEFAULT_LEVEL;
     protected static final int DEFAULT_QS_SIGNAL_STRENGTH = DEFAULT_LEVEL;
     protected static final int DEFAULT_ICON = TelephonyIcons.ICON_3G;
-    protected static final int DEFAULT_QS_ICON = TelephonyIcons.QS_DATA_3G;
+    protected static final int DEFAULT_QS_ICON = DEFAULT_ICON;
 
     protected NetworkControllerImpl mNetworkController;
     protected MobileSignalController mMobileSignalController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index dfe00f9..ac64263 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -25,7 +25,7 @@
         setupDefaultSignal();
 
         verifyDataIndicators(TelephonyIcons.ICON_3G,
-                TelephonyIcons.QS_DATA_3G);
+                TelephonyIcons.ICON_3G);
     }
 
     @Test
@@ -35,7 +35,7 @@
                 TelephonyManager.NETWORK_TYPE_GSM);
 
         verifyDataIndicators(TelephonyIcons.ICON_G,
-                TelephonyIcons.QS_DATA_G);
+                TelephonyIcons.ICON_G);
     }
 
     @Test
@@ -45,7 +45,7 @@
                 TelephonyManager.NETWORK_TYPE_CDMA);
 
         verifyDataIndicators(TelephonyIcons.ICON_1X,
-                TelephonyIcons.QS_DATA_1X);
+                TelephonyIcons.ICON_1X);
     }
 
     @Test
@@ -55,7 +55,7 @@
                 TelephonyManager.NETWORK_TYPE_EDGE);
 
         verifyDataIndicators(TelephonyIcons.ICON_E,
-                TelephonyIcons.QS_DATA_E);
+                TelephonyIcons.ICON_E);
     }
 
     @Test
@@ -65,7 +65,7 @@
                 TelephonyManager.NETWORK_TYPE_LTE);
 
         verifyDataIndicators(TelephonyIcons.ICON_LTE,
-                TelephonyIcons.QS_DATA_LTE);
+                TelephonyIcons.ICON_LTE);
     }
 
     @Test
@@ -75,7 +75,7 @@
                 TelephonyManager.NETWORK_TYPE_HSPA);
 
         verifyDataIndicators(TelephonyIcons.ICON_H,
-                TelephonyIcons.QS_DATA_H);
+                TelephonyIcons.ICON_H);
     }
 
     @Test
@@ -104,7 +104,7 @@
                 TelephonyManager.NETWORK_TYPE_LTE);
 
         verifyDataIndicators(TelephonyIcons.ICON_4G,
-                TelephonyIcons.QS_DATA_4G);
+                TelephonyIcons.ICON_4G);
     }
 
     @Ignore("Flaky")
@@ -117,7 +117,7 @@
         setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
 
         verifyDataIndicators(TelephonyIcons.ICON_DATA_DISABLED,
-                TelephonyIcons.QS_ICON_DATA_DISABLED);
+                TelephonyIcons.ICON_DATA_DISABLED);
     }
 
     @Test
@@ -148,7 +148,7 @@
         mNetworkController.handleConfigurationChanged();
 
         verifyDataIndicators(TelephonyIcons.ICON_4G,
-                TelephonyIcons.QS_DATA_4G);
+                TelephonyIcons.ICON_4G);
     }
 
     @Test
@@ -158,13 +158,13 @@
                 TelephonyManager.NETWORK_TYPE_LTE);
 
         verifyDataIndicators(TelephonyIcons.ICON_LTE,
-                TelephonyIcons.QS_DATA_LTE);
+                TelephonyIcons.ICON_LTE);
 
         when(mServiceState.getDataNetworkType())
                 .thenReturn(TelephonyManager.NETWORK_TYPE_HSPA);
         updateServiceState();
         verifyDataIndicators(TelephonyIcons.ICON_H,
-                TelephonyIcons.QS_DATA_H);
+                TelephonyIcons.ICON_H);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 1627925..aa62075 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -214,7 +214,7 @@
 
             verifyLastQsMobileDataIndicators(true,
                     testStrength,
-                    TelephonyIcons.QS_DATA_1X, false, false);
+                    TelephonyIcons.ICON_1X, false, false);
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
new file mode 100644
index 0000000..8124bf39
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.service.notification.ZenModeConfig;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.ZenModeController.Callback;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class ZenModeControllerImplTest extends SysuiTestCase {
+
+    private Callback mCallback;
+
+    @Test
+    public void testRemoveDuringCallback() {
+        ZenModeControllerImpl controller = new ZenModeControllerImpl(mContext, new Handler());
+        mCallback = new Callback() {
+            @Override
+            public void onConfigChanged(ZenModeConfig config) {
+                controller.removeCallback(mCallback);
+            }
+        };
+        controller.addCallback(mCallback);
+        Callback mockCallback = mock(Callback.class);
+        controller.addCallback(mockCallback);
+        controller.fireConfigChanged(null);
+        verify(mockCallback).onConfigChanged(eq(null));
+    }
+
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
index 02daa44..bc6833d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
@@ -61,4 +61,12 @@
         Assert.assertTrue(lowPriorityHeaderView.getParent() == null);
         Assert.assertTrue(childrenContainer.getLowPriorityHeaderView() == null);
     }
+
+    @Test
+    public void testRecreateNotificationHeader_hasHeader() {
+        NotificationChildrenContainer childrenContainer = mGroup.getChildrenContainer();
+        childrenContainer.recreateNotificationHeader(null);
+        Assert.assertNotNull("Children container must have a header after recreation",
+                childrenContainer.getCurrentHeaderView());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
index 3a0f907..daf7547 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
@@ -111,7 +111,10 @@
 
         @Override
         public void addCallback(Consumer<T> callback) {
+        }
 
+        public T reload() {
+            return null;
         }
     }
 }
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 1670ba0..ddc9e6c 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4034,6 +4034,11 @@
     // FIELD - Number of results when Settings search result is clicked
     FIELD_SETTINGS_SERACH_RESULT_COUNT = 1002;
 
+    // OPEN: Settings > Display > Ambient Display
+    // CATEGORY: SETTINGS
+    // OS: O DR
+    AMBIENT_DISPLAY_SETTINGS = 1003;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index fa78f10..9e2f52a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -487,15 +487,6 @@
                 if (pip != null) {
                     int pipId = pip.getId();
                     event.setWindowId(pipId);
-                    event.setSealed(true);
-                    AccessibilityNodeInfo info = event.getSource();
-                    info.setSealed(false);
-                    event.setSealed(false);
-                    if (info != null) {
-                        info.setSourceNodeId(info.getSourceNodeId(), pipId);
-                        event.setSource(info);
-                        info.recycle();
-                    }
                 }
             }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 93b5ed5..03f25bf 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -711,10 +711,13 @@
             ContentResolver resolver = mContext.getContentResolver();
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.AUTOFILL_SERVICE), false, this, UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.USER_SETUP_COMPLETE), false, this, UserHandle.USER_ALL);
         }
 
         @Override
         public void onChange(boolean selfChange, Uri uri, int userId) {
+            if (sVerbose) Slog.v(TAG, "onChange(): uri=" + uri + ", userId=" + userId);
             synchronized (mLock) {
                 updateCachedServiceLocked(userId);
             }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 2cb0bd5..7abaf7f 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -51,7 +51,6 @@
 import android.service.autofill.FillResponse;
 import android.service.autofill.IAutoFillService;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.LocalLog;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -99,6 +98,12 @@
      */
     private boolean mDisabled;
 
+    /**
+     * Caches whether the setup completed for the current user.
+     */
+    @GuardedBy("mLock")
+    private boolean mSetupComplete;
+
     private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
         switch (msg.what) {
             case MSG_SERVICE_SAVE:
@@ -171,6 +176,12 @@
         }
     }
 
+    private boolean isSetupCompletedLocked() {
+        final String setupComplete = Settings.Secure.getStringForUser(
+                mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId);
+        return "1".equals(setupComplete);
+    }
+
     private String getComponentNameFromSettings() {
         return Settings.Secure.getStringForUser(
                 mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
@@ -178,6 +189,12 @@
 
     void updateLocked(boolean disabled) {
         final boolean wasEnabled = isEnabled();
+        if (sVerbose) {
+            Slog.v(TAG, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled
+                    + ", mSetupComplete= " + mSetupComplete
+                    + ", disabled=" + disabled + ", mDisabled=" + mDisabled);
+        }
+        mSetupComplete = isSetupCompletedLocked();
         mDisabled = disabled;
         ComponentName serviceComponent = null;
         ServiceInfo serviceInfo = null;
@@ -199,8 +216,9 @@
             } else {
                 mInfo = null;
             }
-            if (wasEnabled != isEnabled()) {
-                if (!isEnabled()) {
+            final boolean isEnabled = isEnabled();
+            if (wasEnabled != isEnabled) {
+                if (!isEnabled) {
                     final int sessionCount = mSessions.size();
                     for (int i = sessionCount - 1; i >= 0; i--) {
                         final Session session = mSessions.valueAt(i);
@@ -534,6 +552,7 @@
         pw.print(prefix); pw.print("Default component: ");
             pw.println(mContext.getString(R.string.config_defaultAutofillService));
         pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+        pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
         pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
 
         final int size = mSessions.size();
@@ -617,7 +636,7 @@
     }
 
     boolean isEnabled() {
-        return mInfo != null && !mDisabled;
+        return mSetupComplete && mInfo != null && !mDisabled;
     }
 
     @Override
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 38db8de..f1f8757 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -839,6 +839,29 @@
         return !appGetsFullBackup(pkg);
     }
 
+    /*
+     * Construct a backup agent instance for the metadata pseudopackage.  This is a
+     * process-local non-lifecycle agent instance, so we manually set up the context
+     * topology for it.
+     */
+    PackageManagerBackupAgent makeMetadataAgent() {
+        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
+        pmAgent.attach(mContext);
+        pmAgent.onCreate();
+        return pmAgent;
+    }
+
+    /*
+     * Same as above but with the explicit package-set configuration.
+     */
+    PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
+        PackageManagerBackupAgent pmAgent =
+                new PackageManagerBackupAgent(mPackageManager, packages);
+        pmAgent.attach(mContext);
+        pmAgent.onCreate();
+        return pmAgent;
+    }
+
     // ----- Asynchronous backup/restore handler thread -----
 
     private class BackupHandler extends Handler {
@@ -2893,8 +2916,7 @@
                     // because it's cheap and this way we guarantee that we don't get out of
                     // step even if we're selecting among various transports at run time.
                     if (mStatus == BackupTransport.TRANSPORT_OK) {
-                        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
-                                mPackageManager);
+                        PackageManagerBackupAgent pmAgent = makeMetadataAgent();
                         mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
                                 IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
                         addBackupTrace("PMBA invoke: " + mStatus);
@@ -7155,7 +7177,7 @@
             mObserver = observer;
             mLatchObject = latch;
             mAgent = null;
-            mPackageManagerBackupAgent = new PackageManagerBackupAgent(mPackageManager);
+            mPackageManagerBackupAgent = makeMetadataAgent();
             mAgentPackage = null;
             mTargetApp = null;
             mObbConnection = new FullBackupObbConnection();
@@ -8817,7 +8839,7 @@
                 // Pull the Package Manager metadata from the restore set first
                 mCurrentPackage = new PackageInfo();
                 mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
-                mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
+                mPmAgent = makeMetadataAgent(null);
                 mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind());
                 if (MORE_DEBUG) {
                     Slog.v(TAG, "initiating restore for PMBA");
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b4f27f5..224845c 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -180,6 +180,7 @@
 
     public static final String DIAG_ARG = "--diag";
     public static final String SHORT_ARG = "--short";
+    public static final String TETHERING_ARG = "tethering";
 
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
@@ -209,6 +210,13 @@
     // See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
     private final int mReleasePendingIntentDelayMs;
 
+    // Driver specific constants used to select packets received via
+    // WiFi that caused the phone to exit sleep state. Currently there
+    // is only one kernel implementation so we can get away with
+    // constants.
+    private static final int mWakeupPacketMark = 0x80000000;
+    private static final int mWakeupPacketMask = 0x80000000;
+
     private MockableSystemProperties mSystemProperties;
 
     private Tethering mTethering;
@@ -1835,7 +1843,7 @@
 
     private boolean argsContain(String[] args, String target) {
         for (String arg : args) {
-            if (arg.equals(target)) return true;
+            if (target.equals(arg)) return true;
         }
         return false;
     }
@@ -1866,6 +1874,9 @@
         if (argsContain(args, DIAG_ARG)) {
             dumpNetworkDiagnostics(pw);
             return;
+        } else if (argsContain(args, TETHERING_ARG)) {
+            mTethering.dump(fd, pw, args);
+            return;
         }
 
         pw.print("NetworkFactories for:");
@@ -4352,7 +4363,7 @@
             networkAgent.clatd.fixupLinkProperties(oldLp);
         }
 
-        updateInterfaces(newLp, oldLp, netId);
+        updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities);
         updateMtu(newLp, oldLp);
         // TODO - figure out what to do for clat
 //        for (LinkProperties lp : newLp.getStackedLinks()) {
@@ -4390,7 +4401,26 @@
         }
     }
 
-    private void updateInterfaces(LinkProperties newLp, LinkProperties oldLp, int netId) {
+    private void wakeupAddInterface(String iface, NetworkCapabilities caps) throws RemoteException {
+        // Marks are only available on WiFi interaces. Checking for
+        // marks on unsupported interfaces is harmless.
+        if (!caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+            return;
+        }
+        mNetd.getNetdService().wakeupAddInterface(
+            iface, "iface:" + iface, mWakeupPacketMark, mWakeupPacketMask);
+    }
+
+    private void wakeupDelInterface(String iface, NetworkCapabilities caps) throws RemoteException {
+        if (!caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+            return;
+        }
+        mNetd.getNetdService().wakeupDelInterface(
+            iface, "iface:" + iface, mWakeupPacketMark, mWakeupPacketMask);
+    }
+
+    private void updateInterfaces(LinkProperties newLp, LinkProperties oldLp, int netId,
+                                  NetworkCapabilities caps) {
         CompareResult<String> interfaceDiff = new CompareResult<String>();
         if (oldLp != null) {
             interfaceDiff = oldLp.compareAllInterfaceNames(newLp);
@@ -4401,6 +4431,7 @@
             try {
                 if (DBG) log("Adding iface " + iface + " to network " + netId);
                 mNetd.addInterfaceToNetwork(iface, netId);
+                wakeupAddInterface(iface, caps);
             } catch (Exception e) {
                 loge("Exception adding interface: " + e);
             }
@@ -4409,6 +4440,7 @@
             try {
                 if (DBG) log("Removing iface " + iface + " from network " + netId);
                 mNetd.removeInterfaceFromNetwork(iface, netId);
+                wakeupDelInterface(iface, caps);
             } catch (Exception e) {
                 loge("Exception removing interface: " + e);
             }
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 6502c01..68f8c1b 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -41,14 +41,10 @@
 # ---------------------------
 # DeviceStorageMonitorService.java
 # ---------------------------
-# The disk space free on the /data partition, in bytes
-2744 free_storage_changed (data|2|2)
-# Device low memory notification and disk space free on the /data partition, in bytes at that time
-2745 low_storage (data|2|2)
-# disk space free on the /data, /system, and /cache partitions in bytes
-2746 free_storage_left (data|2|2),(system|2|2),(cache|2|2)
-# file on cache partition was deleted
+# File on cache partition was deleted
 2748 cache_file_deleted (path|3)
+# Storage volume state and usable space in bytes
+2749 storage_state (uuid|3),(old_state|1),(new_state|1),(usable|2),(total|2)
 
 
 # ---------------------------
diff --git a/services/core/java/com/android/server/FgThread.java b/services/core/java/com/android/server/FgThread.java
index 5f85cba..18fb477 100644
--- a/services/core/java/com/android/server/FgThread.java
+++ b/services/core/java/com/android/server/FgThread.java
@@ -45,14 +45,14 @@
     }
 
     public static FgThread get() {
-        synchronized (UiThread.class) {
+        synchronized (FgThread.class) {
             ensureThreadLocked();
             return sInstance;
         }
     }
 
     public static Handler getHandler() {
-        synchronized (UiThread.class) {
+        synchronized (FgThread.class) {
             ensureThreadLocked();
             return sHandler;
         }
diff --git a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
new file mode 100644
index 0000000..80f8e51
--- /dev/null
+++ b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+/**
+ * Internal interface for storing and retrieving persistent data.
+ */
+public interface PersistentDataBlockManagerInternal {
+
+    /** Stores the handle to a lockscreen credential to be used for Factory Reset Protection. */
+    void setFrpCredentialHandle(byte[] handle);
+
+    /** Retrieves handle to a lockscreen credential to be used for Factory Reset Protection. */
+    byte[] getFrpCredentialHandle();
+}
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index e3cd87c..1d4c3db 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -32,6 +32,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
 
 import libcore.io.IoUtils;
 
@@ -71,8 +72,13 @@
     private static final int HEADER_SIZE = 8;
     // Magic number to mark block device as adhering to the format consumed by this service
     private static final int PARTITION_TYPE_MARKER = 0x19901873;
+    /** Size of the block reserved for FPR credential, including 4 bytes for the size header. */
+    private static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
+    /** Maximum size of the FRP credential handle that can be stored. */
+    private static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
     // Limit to 100k as blocks larger than this might cause strain on Binder.
     private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
+
     public static final int DIGEST_SIZE_BYTES = 32;
     private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
     private static final String FLASH_LOCK_PROP = "ro.boot.flash.locked";
@@ -136,6 +142,7 @@
                 Thread.currentThread().interrupt();
                 throw new IllegalStateException("Service " + TAG + " init interrupted", e);
             }
+            LocalServices.addService(PersistentDataBlockManagerInternal.class, mInternalService);
         }
         super.onBootPhase(phase);
     }
@@ -382,7 +389,7 @@
             enforceUid(Binder.getCallingUid());
 
             // Need to ensure we don't write over the last byte
-            long maxBlockSize = getBlockDeviceSize() - HEADER_SIZE - 1;
+            long maxBlockSize = getMaximumDataBlockSize();
             if (data.length > maxBlockSize) {
                 // partition is ~500k so shouldn't be a problem to downcast
                 return (int) -maxBlockSize;
@@ -562,8 +569,99 @@
 
         @Override
         public long getMaximumDataBlockSize() {
-            long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1;
+            long actualSize = getBlockDeviceSize() - HEADER_SIZE - DIGEST_SIZE_BYTES
+                    - FRP_CREDENTIAL_RESERVED_SIZE - 1;
             return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
         }
+
+        @Override
+        public boolean hasFrpCredentialHandle() {
+            enforcePersistentDataBlockAccess();
+            return mInternalService.getFrpCredentialHandle() != null;
+        }
+    };
+
+    private PersistentDataBlockManagerInternal mInternalService =
+            new PersistentDataBlockManagerInternal() {
+
+        @Override
+        public void setFrpCredentialHandle(byte[] handle) {
+            Preconditions.checkArgument(handle == null || handle.length > 0,
+                    "handle must be null or non-empty");
+            Preconditions.checkArgument(handle == null
+                            || handle.length <= MAX_FRP_CREDENTIAL_HANDLE_SIZE,
+                    "handle must not be longer than " + MAX_FRP_CREDENTIAL_HANDLE_SIZE);
+
+            FileOutputStream outputStream;
+            try {
+                outputStream = new FileOutputStream(new File(mDataBlockFile));
+            } catch (FileNotFoundException e) {
+                Slog.e(TAG, "partition not available", e);
+                return;
+            }
+
+            ByteBuffer data = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
+            data.putInt(handle == null ? 0 : handle.length);
+            if (handle != null) {
+                data.put(handle);
+            }
+            data.flip();
+
+            synchronized (mLock) {
+                if (!mIsWritable) {
+                    IoUtils.closeQuietly(outputStream);
+                    return;
+                }
+
+                try {
+                    FileChannel channel = outputStream.getChannel();
+
+                    channel.position(getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE);
+                    channel.write(data);
+                    outputStream.flush();
+                } catch (IOException e) {
+                    Slog.e(TAG, "unable to access persistent partition", e);
+                    return;
+                } finally {
+                    IoUtils.closeQuietly(outputStream);
+                }
+
+                computeAndWriteDigestLocked();
+            }
+        }
+
+        @Override
+        public byte[] getFrpCredentialHandle() {
+            if (!enforceChecksumValidity()) {
+                return null;
+            }
+
+            DataInputStream inputStream;
+            try {
+                inputStream = new DataInputStream(
+                        new FileInputStream(new File(mDataBlockFile)));
+            } catch (FileNotFoundException e) {
+                Slog.e(TAG, "partition not available");
+                return null;
+            }
+
+            try {
+                synchronized (mLock) {
+                    inputStream.skip(getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE);
+                    int length = inputStream.readInt();
+                    if (length <= 0 || length > MAX_FRP_CREDENTIAL_HANDLE_SIZE) {
+                        return null;
+                    }
+                    byte[] bytes = new byte[length];
+                    inputStream.readFully(bytes);
+                    return bytes;
+                }
+            } catch (IOException e) {
+                Slog.e(TAG, "unable to access persistent partition", e);
+                return null;
+            } finally {
+                IoUtils.closeQuietly(inputStream);
+            }
+        }
     };
 }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index cffb158..35b452a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3296,6 +3296,9 @@
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
         final StorageStatsManager stats = mContext.getSystemService(StorageStatsManager.class);
 
+        // Apps can't defy reserved space
+        flags &= ~StorageManager.FLAG_ALLOCATE_DEFY_RESERVED;
+
         final boolean aggressive = (flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0;
         if (aggressive) {
             mContext.enforceCallingOrSelfPermission(
@@ -3306,24 +3309,31 @@
         try {
             // In general, apps can allocate as much space as they want, except
             // we never let them eat into either the minimum cache space or into
-            // the low disk warning space.
+            // the low disk warning space. To avoid user confusion, this logic
+            // should be kept in sync with getFreeBytes().
             final File path = storage.findPathForUuid(volumeUuid);
+
+            final long usable = path.getUsableSpace();
+            final long lowReserved = storage.getStorageLowBytes(path);
+            final long fullReserved = storage.getStorageFullBytes(path);
+
             if (stats.isQuotaSupported(volumeUuid)) {
+                final long cacheTotal = stats.getCacheBytes(volumeUuid);
+                final long cacheReserved = storage.getStorageCacheBytes(path);
+                final long cacheClearable = Math.max(0, cacheTotal - cacheReserved);
+
                 if (aggressive) {
-                    return Math.max(0,
-                            stats.getFreeBytes(volumeUuid) - storage.getStorageFullBytes(path));
+                    return Math.max(0, (usable + cacheTotal) - fullReserved);
                 } else {
-                    return Math.max(0,
-                            stats.getFreeBytes(volumeUuid) - storage.getStorageLowBytes(path)
-                                    - storage.getStorageCacheBytes(path));
+                    return Math.max(0, (usable + cacheClearable) - lowReserved);
                 }
             } else {
                 // When we don't have fast quota information, we ignore cached
                 // data and only consider unused bytes.
                 if (aggressive) {
-                    return Math.max(0, path.getUsableSpace() - storage.getStorageFullBytes(path));
+                    return Math.max(0, usable - fullReserved);
                 } else {
-                    return Math.max(0, path.getUsableSpace() - storage.getStorageLowBytes(path));
+                    return Math.max(0, usable - lowReserved);
                 }
             }
         } catch (IOException e) {
@@ -3337,6 +3347,9 @@
     public void allocateBytes(String volumeUuid, long bytes, int flags) {
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
 
+        // Apps can't defy reserved space
+        flags &= ~StorageManager.FLAG_ALLOCATE_DEFY_RESERVED;
+
         // This method call will enforce FLAG_ALLOCATE_AGGRESSIVE permissions so
         // we don't have to enforce them locally
         final long allocatableBytes = getAllocatableBytes(volumeUuid, flags);
@@ -3350,7 +3363,11 @@
             // Free up enough disk space to satisfy both the requested allocation
             // and our low disk warning space.
             final File path = storage.findPathForUuid(volumeUuid);
-            bytes += storage.getStorageLowBytes(path);
+            if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) {
+                bytes += storage.getStorageFullBytes(path);
+            } else {
+                bytes += storage.getStorageLowBytes(path);
+            }
 
             mPms.freeStorage(volumeUuid, bytes, flags);
         } catch (IOException e) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8fe8ddb..248c93a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8432,8 +8432,8 @@
                         synchronized (mPidsSelfLocked) {
                             proc = mPidsSelfLocked.get(callingPid);
                         }
-                        if (proc != null && proc.curProcState
-                                < ActivityManager.PROCESS_STATE_RECEIVER) {
+                        if (proc != null &&
+                                !ActivityManager.isProcStateBackground(proc.curProcState)) {
                             // Whoever is instigating this is in the foreground, so we will allow it
                             // to go through.
                             return ActivityManager.APP_START_MODE_NORMAL;
@@ -13524,7 +13524,9 @@
                         return;
                     }
                     if (pr.hasTopUi != hasTopUi) {
-                        Slog.i(TAG, "Setting hasTopUi=" + hasTopUi + " for pid=" + pid);
+                        if (DEBUG_OOM_ADJ) {
+                            Slog.d(TAG, "Setting hasTopUi=" + hasTopUi + " for pid=" + pid);
+                        }
                         pr.hasTopUi = hasTopUi;
                         changed = true;
                     }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 2d70109..a45becd 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -244,7 +244,7 @@
     static final int REMOVE_TASK_MODE_MOVING = 1;
     // Similar to {@link #REMOVE_TASK_MODE_MOVING} and the task will be added to the top of its new
     // stack and the new stack will be on top of all stacks.
-    private static final int REMOVE_TASK_MODE_MOVING_TO_TOP = 2;
+    static final int REMOVE_TASK_MODE_MOVING_TO_TOP = 2;
 
     // The height/width divide used when fitting a task within a bounds with method
     // {@link #fitWithinBounds}.
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 84aae7b..473c3c2 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3005,8 +3005,8 @@
         // Calculate the default bounds (don't use existing stack bounds as we may have just created
         // the stack, and schedule the start of the animation into PiP (the bounds animator that
         // is triggered by this is posted on another thread)
-        final Rect destBounds = stack.getPictureInPictureBounds(aspectRatio,
-                false /* useExistingStackBounds */);
+        final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
+
         stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
                 true /* fromFullscreen */);
 
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 7ed07e0..07caf9e 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1432,7 +1432,13 @@
                     + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
             mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
             mNewTaskInfo = mSourceRecord.info;
-            mNewTaskIntent = mSourceRecord.getTask().intent;
+
+            // It is not guaranteed that the source record will have a task associated with it. For,
+            // example, if this method is being called for processing a pending activity launch, it
+            // is possible that the activity has been removed from the task after the launch was
+            // enqueued.
+            final TaskRecord sourceTask = mSourceRecord.getTask();
+            mNewTaskIntent = sourceTask != null ? sourceTask.intent : null;
         }
         mSourceRecord = null;
         mSourceStack = null;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 2439062..e15b135 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -87,6 +87,7 @@
         implements PowerManagerInternal.LowPowerModeListener,
         BatteryStatsImpl.PlatformIdleStateCallback {
     static final String TAG = "BatteryStatsService";
+    static final boolean DBG = false;
 
     /**
      * How long to wait on an individual subsystem to return its stats.
@@ -152,11 +153,11 @@
 
                 case MSG_WRITE_TO_DISK:
                     updateExternalStatsSync("write", UPDATE_ALL);
-                    Slog.d(TAG, "begin writeAsyncLocked");
+                    if (DBG) Slog.d(TAG, "begin writeAsyncLocked");
                     synchronized (mStats) {
                         mStats.writeAsyncLocked();
                     }
-                    Slog.d(TAG, "end writeAsyncLocked");
+                    if (DBG) Slog.d(TAG, "end writeAsyncLocked");
                     break;
             }
         }
@@ -196,7 +197,7 @@
 
     @Override
     public String getPlatformLowPowerStats() {
-        Slog.d(TAG, "begin getPlatformLowPowerStats");
+        if (DBG) Slog.d(TAG, "begin getPlatformLowPowerStats");
         try {
             mUtf8BufferStat.clear();
             mUtf16BufferStat.clear();
@@ -212,7 +213,7 @@
             mUtf16BufferStat.flip();
             return mUtf16BufferStat.toString();
         } finally {
-            Slog.d(TAG, "end getPlatformLowPowerStats");
+            if (DBG) Slog.d(TAG, "end getPlatformLowPowerStats");
         }
     }
 
@@ -561,11 +562,11 @@
         
     public void noteScreenState(int state) {
         enforceCallingPermission();
-        Slog.d(TAG, "begin noteScreenState");
+        if (DBG) Slog.d(TAG, "begin noteScreenState");
         synchronized (mStats) {
             mStats.noteScreenStateLocked(state);
         }
-        Slog.d(TAG, "end noteScreenState");
+        if (DBG) Slog.d(TAG, "end noteScreenState");
     }
     
     public void noteScreenBrightness(int brightness) {
@@ -718,11 +719,11 @@
 
     public void noteStartCamera(int uid) {
         enforceCallingPermission();
-        Slog.d(TAG, "begin noteStartCamera");
+        if (DBG) Slog.d(TAG, "begin noteStartCamera");
         synchronized (mStats) {
             mStats.noteCameraOnLocked(uid);
         }
-        Slog.d(TAG, "end noteStartCamera");
+        if (DBG) Slog.d(TAG, "end noteStartCamera");
     }
 
     public void noteStopCamera(int uid) {
@@ -1342,23 +1343,23 @@
                     }
                 }
             }
-            Slog.d(TAG, "begin dumpCheckinLocked from UID " + Binder.getCallingUid());
+            if (DBG) Slog.d(TAG, "begin dumpCheckinLocked from UID " + Binder.getCallingUid());
             synchronized (mStats) {
                 mStats.dumpCheckinLocked(mContext, pw, apps, flags, historyStart);
                 if (writeData) {
                     mStats.writeAsyncLocked();
                 }
             }
-            Slog.d(TAG, "end dumpCheckinLocked");
+            if (DBG) Slog.d(TAG, "end dumpCheckinLocked");
         } else {
-            Slog.d(TAG, "begin dumpLocked from UID " + Binder.getCallingUid());
+            if (DBG) Slog.d(TAG, "begin dumpLocked from UID " + Binder.getCallingUid());
             synchronized (mStats) {
                 mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
                 if (writeData) {
                     mStats.writeAsyncLocked();
                 }
             }
-            Slog.d(TAG, "end dumpLocked");
+            if (DBG) Slog.d(TAG, "end dumpLocked");
         }
     }
 
@@ -1480,11 +1481,11 @@
         SynchronousResultReceiver bluetoothReceiver = null;
         SynchronousResultReceiver modemReceiver = null;
 
-        Slog.d(TAG, "begin updateExternalStatsSync reason=" + reason);
+        if (DBG) Slog.d(TAG, "begin updateExternalStatsSync reason=" + reason);
         synchronized (mExternalStatsLock) {
             if (mContext == null) {
                 // Don't do any work yet.
-                Slog.d(TAG, "end updateExternalStatsSync");
+                if (DBG) Slog.d(TAG, "end updateExternalStatsSync");
                 return;
             }
 
@@ -1583,7 +1584,7 @@
                 }
             }
         }
-        Slog.d(TAG, "end updateExternalStatsSync");
+        if (DBG) Slog.d(TAG, "end updateExternalStatsSync");
     }
 
     /**
@@ -1603,7 +1604,7 @@
                 return getHealthStatsForUidLocked(requestUid);
             }
         } catch (Exception ex) {
-            Slog.d(TAG, "Crashed while writing for takeUidSnapshot(" + requestUid + ")", ex);
+            Slog.w(TAG, "Crashed while writing for takeUidSnapshot(" + requestUid + ")", ex);
             throw ex;
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -1633,7 +1634,7 @@
                 return results;
             }
         } catch (Exception ex) {
-            Slog.d(TAG, "Crashed while writing for takeUidSnapshots("
+            if (DBG) Slog.d(TAG, "Crashed while writing for takeUidSnapshots("
                     + Arrays.toString(requestUids) + ") i=" + i, ex);
             throw ex;
         } finally {
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 144eb11..2e0ec0b 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -97,7 +97,7 @@
         mKeyguardShowing = showing;
         dismissDockedStackIfNeeded();
         if (showing) {
-            mKeyguardGoingAway = false;
+            setKeyguardGoingAway(false);
             mDismissalRequested = false;
         }
         mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
@@ -114,7 +114,7 @@
         if (mKeyguardShowing) {
             mWindowManager.deferSurfaceLayout();
             try {
-                mKeyguardGoingAway = true;
+                setKeyguardGoingAway(true);
                 mWindowManager.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
                         false /* alwaysKeepCurrent */, convertTransitFlags(flags),
                         false /* forceOverride */);
@@ -139,6 +139,11 @@
         mWindowManager.dismissKeyguard(callback);
     }
 
+    private void setKeyguardGoingAway(boolean keyguardGoingAway) {
+        mKeyguardGoingAway = keyguardGoingAway;
+        mWindowManager.setKeyguardGoingAway(keyguardGoingAway);
+    }
+
     private void failCallback(IKeyguardDismissCallback callback) {
         try {
             callback.onDismissError();
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index 702bf92..2010c24 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -44,9 +44,9 @@
         return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds);
     }
 
-    Rect getPictureInPictureBounds(float aspectRatio, boolean useExistingStackBounds) {
+    Rect getDefaultPictureInPictureBounds(float aspectRatio) {
         return getWindowContainerController().getPictureInPictureBounds(aspectRatio,
-                useExistingStackBounds);
+                null /* currentStackBounds */);
     }
 
     void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration,
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index deb3b28..39aed7c 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -233,7 +233,8 @@
                 updateFile();
             }
             mLastWriteTime = SystemClock.uptimeMillis();
-            Slog.i(TAG, "Prepared write state in " + (SystemClock.uptimeMillis()-now) + "ms");
+            if (DEBUG) Slog.d(TAG, "Prepared write state in "
+                    + (SystemClock.uptimeMillis()-now) + "ms");
             if (!sync) {
                 BackgroundThread.getHandler().post(new Runnable() {
                     @Override public void run() {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 37d3cd4..4f211e3 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -115,6 +115,7 @@
 import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.STARTING_WINDOW_SHOWN;
 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING_TO_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 
@@ -673,8 +674,11 @@
             mWindowContainerController.reparent(toStack.getWindowContainerController(), position,
                     moveStackMode == REPARENT_MOVE_STACK_TO_FRONT);
 
+            final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT
+                    || (moveStackMode == REPARENT_KEEP_STACK_AT_FRONT && (wasFocused || wasFront));
             // Move the task
-            sourceStack.removeTask(this, reason, REMOVE_TASK_MODE_MOVING);
+            sourceStack.removeTask(this, reason, moveStackToFront
+                    ? REMOVE_TASK_MODE_MOVING_TO_TOP : REMOVE_TASK_MODE_MOVING);
             toStack.addTask(this, position, false /* schedulePictureInPictureModeChange */, reason);
 
             if (schedulePictureInPictureModeChange) {
@@ -693,8 +697,6 @@
 
             // If the task had focus before (or we're requested to move focus), move focus to the
             // new stack by moving the stack to the front.
-            final boolean moveStackToFront = moveStackMode == REPARENT_MOVE_STACK_TO_FRONT
-                    || (moveStackMode == REPARENT_KEEP_STACK_AT_FRONT && (wasFocused || wasFront));
             if (r != null) {
                 toStack.moveToFrontAndResumeStateIfNeeded(r, moveStackToFront, wasResumed,
                         wasPaused, reason);
@@ -2334,7 +2336,7 @@
                 pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode));
                 pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture);
                 pw.print(" isResizeable=" + isResizeable());
-                pw.print(" firstActiveTime=" + lastActiveTime);
+                pw.print(" firstActiveTime=" + firstActiveTime);
                 pw.print(" lastActiveTime=" + lastActiveTime);
                 pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
     }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 6a310f2..ba8fa31 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -123,12 +123,13 @@
     private final Injector mInjector;
     private final Handler mHandler;
 
-    // Holds the current foreground user's id
+    // Holds the current foreground user's id. Use mLock when updating
     @GuardedBy("mLock")
-    private int mCurrentUserId = UserHandle.USER_SYSTEM;
-    // Holds the target user's id during a user switch
+    private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
+    // Holds the target user's id during a user switch. The value of mCurrentUserId will be updated
+    // once target user goes into the foreground. Use mLock when updating
     @GuardedBy("mLock")
-    private int mTargetUserId = UserHandle.USER_NULL;
+    private volatile int mTargetUserId = UserHandle.USER_NULL;
 
     /**
      * Which users have been started, so are allowed to run code.
@@ -1505,6 +1506,11 @@
             Slog.w(TAG, msg);
             throw new SecurityException(msg);
         }
+
+        // Optimization - if there is no pending user switch, return current id
+        if (mTargetUserId == UserHandle.USER_NULL) {
+            return getUserInfo(mCurrentUserId);
+        }
         synchronized (mLock) {
             return getCurrentUserLocked();
         }
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 214cfce..4094083 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -135,6 +135,10 @@
         }
     }
 
+    @Override
+    public synchronized void onWakeupEvent(String prefix, int uid, int gid, long timestampNs) {
+    }
+
     public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
         flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto);
         flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 08208d7..cb1413e 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -238,7 +238,7 @@
     }
 
     private void updateConfiguration() {
-        mConfig = new TetheringConfiguration(mContext);
+        mConfig = new TetheringConfiguration(mContext, mLog);
     }
 
     @Override
@@ -1696,6 +1696,7 @@
                 pw.println(" - lastError = " + tetherState.lastError);
             }
             pw.println("Upstream wanted: " + upstreamWanted());
+            pw.println("Current upstream interface: " + mCurrentUpstreamIface);
             pw.decreaseIndent();
         }
 
@@ -1713,7 +1714,7 @@
 
     private static boolean argsContain(String[] args, String target) {
         for (String arg : args) {
-            if (arg.equals(target)) return true;
+            if (target.equals(arg)) return true;
         }
         return false;
     }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 6941193..651de89 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -25,7 +25,7 @@
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.telephony.TelephonyManager;
-import android.util.Log;
+import android.net.util.SharedLog;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -74,7 +74,9 @@
     public final String[] dhcpRanges;
     public final String[] defaultIPv4DNS;
 
-    public TetheringConfiguration(Context ctx) {
+    public TetheringConfiguration(Context ctx, SharedLog log) {
+        final SharedLog configLog = log.forSubComponent("config");
+
         tetherableUsbRegexs = ctx.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_usb_regexs);
         tetherableWifiRegexs = ctx.getResources().getStringArray(
@@ -83,11 +85,15 @@
                 com.android.internal.R.array.config_tether_bluetooth_regexs);
 
         final int dunCheck = checkDunRequired(ctx);
+        configLog.log("DUN check returned: " + dunCheckString(dunCheck));
+
         preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck);
         isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN);
 
         dhcpRanges = getDhcpRanges(ctx);
         defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
+
+        configLog.log(toString());
     }
 
     public boolean isUsb(String iface) {
@@ -110,21 +116,25 @@
         pw.print("isDunRequired: ");
         pw.println(isDunRequired);
 
-        String[] upstreamTypes = null;
-        if (preferredUpstreamIfaceTypes != null) {
-            upstreamTypes = new String[preferredUpstreamIfaceTypes.size()];
-            int i = 0;
-            for (Integer netType : preferredUpstreamIfaceTypes) {
-                upstreamTypes[i] = ConnectivityManager.getNetworkTypeName(netType);
-                i++;
-            }
-        }
-        dumpStringArray(pw, "preferredUpstreamIfaceTypes", upstreamTypes);
+        dumpStringArray(pw, "preferredUpstreamIfaceTypes",
+                preferredUpstreamNames(preferredUpstreamIfaceTypes));
 
         dumpStringArray(pw, "dhcpRanges", dhcpRanges);
         dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);
     }
 
+    public String toString() {
+        final StringJoiner sj = new StringJoiner(" ");
+        sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs)));
+        sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs)));
+        sj.add(String.format("tetherableBluetoothRegexs:%s",
+                makeString(tetherableBluetoothRegexs)));
+        sj.add(String.format("isDunRequired:%s", isDunRequired));
+        sj.add(String.format("preferredUpstreamIfaceTypes:%s",
+                makeString(preferredUpstreamNames(preferredUpstreamIfaceTypes))));
+        return String.format("TetheringConfiguration{%s}", sj.toString());
+    }
+
     private static void dumpStringArray(PrintWriter pw, String label, String[] values) {
         pw.print(label);
         pw.print(": ");
@@ -140,11 +150,42 @@
         pw.println();
     }
 
+    private static String makeString(String[] strings) {
+        final StringJoiner sj = new StringJoiner(",", "[", "]");
+        for (String s : strings) sj.add(s);
+        return sj.toString();
+    }
+
+    private static String[] preferredUpstreamNames(Collection<Integer> upstreamTypes) {
+        String[] upstreamNames = null;
+
+        if (upstreamTypes != null) {
+            upstreamNames = new String[upstreamTypes.size()];
+            int i = 0;
+            for (Integer netType : upstreamTypes) {
+                upstreamNames[i] = ConnectivityManager.getNetworkTypeName(netType);
+                i++;
+            }
+        }
+
+        return upstreamNames;
+    }
+
     private static int checkDunRequired(Context ctx) {
         final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
         return (tm != null) ? tm.getTetherApnRequired() : DUN_UNSPECIFIED;
     }
 
+    private static String dunCheckString(int dunCheck) {
+        switch (dunCheck) {
+            case DUN_NOT_REQUIRED: return "DUN_NOT_REQUIRED";
+            case DUN_REQUIRED:     return "DUN_REQUIRED";
+            case DUN_UNSPECIFIED:  return "DUN_UNSPECIFIED";
+            default:
+                return String.format("UNKNOWN (%s)", dunCheck);
+        }
+    }
+
     private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, int dunCheck) {
         final int ifaceTypes[] = ctx.getResources().getIntArray(
                 com.android.internal.R.array.config_tether_upstream_types);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 270dcbe..321b660 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -19,9 +19,12 @@
 import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
 import static android.Manifest.permission.READ_CONTACTS;
 import static android.content.Context.KEYGUARD_SERVICE;
+
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
+import static com.android.internal.widget.LockPatternUtils.USER_FRP;
+import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
 
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
@@ -43,7 +46,9 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -63,7 +68,6 @@
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.provider.Settings.SettingNotFoundException;
-import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.security.keystore.AndroidKeyStoreProvider;
 import android.security.keystore.KeyProperties;
@@ -80,6 +84,7 @@
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
 import com.android.internal.widget.ILockSettings;
 import com.android.internal.widget.LockPatternUtils;
@@ -88,6 +93,7 @@
 import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
+import com.android.server.locksettings.LockSettingsStorage.PersistentData;
 
 import libcore.util.HexEncoding;
 
@@ -105,7 +111,6 @@
 import java.security.SecureRandom;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.CertificateException;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -138,6 +143,9 @@
     // Order of holding lock: mSeparateChallengeLock -> mSpManager -> this
     private final Object mSeparateChallengeLock = new Object();
 
+    private final DeviceProvisionedObserver mDeviceProvisionedObserver =
+            new DeviceProvisionedObserver();
+
     private final Injector mInjector;
     private final Context mContext;
     private final Handler mHandler;
@@ -266,13 +274,13 @@
         try {
             randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40);
             String newPassword = String.valueOf(HexEncoding.encode(randomLockSeed));
+            final int quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
             setLockCredentialInternal(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
-                    managedUserPassword, managedUserId);
+                    managedUserPassword, quality, managedUserId);
             // We store a private credential for the managed user that's unlocked by the primary
             // account holder's credential. As such, the user will never be prompted to enter this
             // password directly, so we always store a password.
-            setLong(LockPatternUtils.PASSWORD_TYPE_KEY,
-                    DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, managedUserId);
+            setLong(LockPatternUtils.PASSWORD_TYPE_KEY, quality, managedUserId);
             tieProfileLockToParent(managedUserId, newPassword);
         } catch (NoSuchAlgorithmException | RemoteException e) {
             Slog.e(TAG, "Fail to tie managed profile", e);
@@ -353,7 +361,7 @@
         }
 
         public SyntheticPasswordManager getSyntheticPasswordManager(LockSettingsStorage storage) {
-            return new SyntheticPasswordManager(storage);
+            return new SyntheticPasswordManager(storage, getUserManager());
         }
 
         public int binderGetCallingUid() {
@@ -547,6 +555,7 @@
         } catch (RemoteException e) {
             Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
         }
+        mDeviceProvisionedObserver.onSystemReady();
         // TODO: maybe skip this for split system user mode.
         mStorage.prefetchUser(UserHandle.USER_SYSTEM);
     }
@@ -783,6 +792,8 @@
     }
 
     private void setStringUnchecked(String key, int userId, String value) {
+        Preconditions.checkArgument(userId != USER_FRP, "cannot store lock settings for FRP user");
+
         mStorage.writeKeyValue(key, value, userId);
         if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) {
             BackupManager.dataChanged("com.android.providers.settings");
@@ -820,6 +831,10 @@
             }
         }
 
+        if (userId == USER_FRP) {
+            return getFrpStringUnchecked(key);
+        }
+
         if (LockPatternUtils.LEGACY_LOCK_PATTERN_ENABLED.equals(key)) {
             key = Settings.Secure.LOCK_PATTERN_ENABLED;
         }
@@ -827,6 +842,17 @@
         return mStorage.readKeyValue(key, defaultValue, userId);
     }
 
+    private String getFrpStringUnchecked(String key) {
+        if (LockPatternUtils.PASSWORD_TYPE_KEY.equals(key)) {
+            return String.valueOf(readFrpPasswordQuality());
+        }
+        return null;
+    }
+
+    private int readFrpPasswordQuality() {
+        return mStorage.readPersistentDataBlock().qualityForUi;
+    }
+
     @Override
     public boolean havePassword(int userId) throws RemoteException {
         synchronized (mSpManager) {
@@ -1036,12 +1062,13 @@
                     // credential, otherwise they get lost
                     if (profilePasswordMap != null && profilePasswordMap.containsKey(managedUserId)) {
                         setLockCredentialInternal(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
-                                profilePasswordMap.get(managedUserId), managedUserId);
+                                profilePasswordMap.get(managedUserId),
+                                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId);
                     } else {
                         Slog.wtf(TAG, "clear tied profile challenges, but no password supplied.");
                         // Supplying null here would lead to untrusted credential change
                         setLockCredentialInternal(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null,
-                                managedUserId);
+                                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId);
                     }
                     mStorage.removeChildProfileLock(managedUserId);
                     removeKeystoreProfileKey(managedUserId);
@@ -1063,18 +1090,19 @@
     // This method should be called by LockPatternUtil only, all internal methods in this class
     // should call setLockCredentialInternal.
     @Override
-    public void setLockCredential(String credential, int type, String savedCredential, int userId)
+    public void setLockCredential(String credential, int type, String savedCredential,
+            int requestedQuality, int userId)
             throws RemoteException {
         checkWritePermission(userId);
         synchronized (mSeparateChallengeLock) {
-            setLockCredentialInternal(credential, type, savedCredential, userId);
+            setLockCredentialInternal(credential, type, savedCredential, requestedQuality, userId);
             setSeparateProfileChallengeEnabled(userId, true, null);
             notifyPasswordChanged(userId);
         }
     }
 
     private void setLockCredentialInternal(String credential, int credentialType,
-            String savedCredential, int userId) throws RemoteException {
+            String savedCredential, int requestedQuality, int userId) throws RemoteException {
         // Normalize savedCredential and credential such that empty string is always represented
         // as null.
         if (TextUtils.isEmpty(savedCredential)) {
@@ -1086,10 +1114,11 @@
         synchronized (mSpManager) {
             if (isSyntheticPasswordBasedCredentialLocked(userId)) {
                 spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
-                        userId);
+                        requestedQuality, userId);
                 return;
             }
         }
+
         if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
             if (credential != null) {
                 Slog.wtf(TAG, "CredentialType is none, but credential is non-null.");
@@ -1101,6 +1130,12 @@
             fixateNewestUserKeyAuth(userId);
             synchronizeUnifiedWorkChallengeForProfiles(userId, null);
             notifyActivePasswordMetricsAvailable(null, userId);
+
+            if (mStorage.getPersistentDataBlock() != null
+                    && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
+                // If owner, write to persistent storage for FRP
+                mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null);
+            }
             return;
         }
         if (credential == null) {
@@ -1133,9 +1168,9 @@
         synchronized (mSpManager) {
             if (shouldMigrateToSyntheticPasswordLocked(userId)) {
                 initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential,
-                        currentHandle.type, userId);
+                        currentHandle.type, requestedQuality, userId);
                 spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
-                        userId);
+                        requestedQuality, userId);
                 return;
             }
         }
@@ -1153,6 +1188,12 @@
             // Refresh the auth token
             doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */);
             synchronizeUnifiedWorkChallengeForProfiles(userId, null);
+            if (mStorage.getPersistentDataBlock() != null
+                    && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
+                // If owner, write to persistent storage for FRP
+                mStorage.writePersistentDataBlock(PersistentData.TYPE_GATEKEEPER, userId,
+                        requestedQuality, willStore.toBytes());
+            }
         } else {
             throw new RemoteException("Failed to enroll " +
                     (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? "password"
@@ -1161,23 +1202,7 @@
     }
 
     private VerifyCredentialResponse convertResponse(GateKeeperResponse gateKeeperResponse) {
-        VerifyCredentialResponse response;
-        int responseCode = gateKeeperResponse.getResponseCode();
-        if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
-            response = new VerifyCredentialResponse(gateKeeperResponse.getTimeout());
-        } else if (responseCode == GateKeeperResponse.RESPONSE_OK) {
-            byte[] token = gateKeeperResponse.getPayload();
-            if (token == null) {
-                // something's wrong if there's no payload with a challenge
-                Slog.e(TAG, "verifyChallenge response had no associated payload");
-                response = VerifyCredentialResponse.ERROR;
-            } else {
-                response = new VerifyCredentialResponse(token);
-            }
-        } else {
-            response = VerifyCredentialResponse.ERROR;
-        }
-        return response;
+        return VerifyCredentialResponse.fromGateKeeperResponse(gateKeeperResponse);
     }
 
     @VisibleForTesting
@@ -1403,6 +1428,11 @@
         if (TextUtils.isEmpty(credential)) {
             throw new IllegalArgumentException("Credential can't be null or empty");
         }
+        if (userId == USER_FRP && Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
+            Slog.e(TAG, "FRP credential can only be verified prior to provisioning.");
+            return VerifyCredentialResponse.ERROR;
+        }
         synchronized (mSpManager) {
             if (isSyntheticPasswordBasedCredentialLocked(userId)) {
                 VerifyCredentialResponse response = spBasedDoVerifyCredentialLocked(credential,
@@ -1413,7 +1443,18 @@
                 return response;
             }
         }
-        CredentialHash storedHash = mStorage.readCredentialHash(userId);
+        final CredentialHash storedHash;
+        if (userId == USER_FRP) {
+            PersistentData data = mStorage.readPersistentDataBlock();
+            if (data.type != PersistentData.TYPE_GATEKEEPER) {
+                Slog.wtf(TAG, "Expected PersistentData.TYPE_GATEKEEPER, but was: " + data.type);
+                return VerifyCredentialResponse.ERROR;
+            }
+            return verifyFrpCredential(credential, credentialType, data, progressCallback);
+        } else {
+            storedHash = mStorage.readCredentialHash(userId);
+        }
+
         if (storedHash.type != credentialType) {
             Slog.wtf(TAG, "doVerifyCredential type mismatch with stored credential??"
                     + " stored: " + storedHash.type + " passed in: " + credentialType);
@@ -1436,13 +1477,37 @@
         if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
             mStrongAuth.reportSuccessfulStrongAuthUnlock(userId);
             if (shouldReEnrollBaseZero) {
-                setLockCredentialInternal(credential, storedHash.type, credentialToVerify, userId);
+                setLockCredentialInternal(credential, storedHash.type, credentialToVerify,
+                        DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId);
             }
         }
 
         return response;
     }
 
+    private VerifyCredentialResponse verifyFrpCredential(String credential, int credentialType,
+            PersistentData data, ICheckCredentialProgressCallback progressCallback)
+            throws RemoteException {
+        CredentialHash storedHash = CredentialHash.fromBytes(data.payload);
+        if (storedHash.type != credentialType) {
+            Slog.wtf(TAG, "doVerifyCredential type mismatch with stored credential??"
+                    + " stored: " + storedHash.type + " passed in: " + credentialType);
+            return VerifyCredentialResponse.ERROR;
+        }
+        if (ArrayUtils.isEmpty(storedHash.hash) || TextUtils.isEmpty(credential)) {
+            Slog.e(TAG, "Stored hash or credential is empty");
+            return VerifyCredentialResponse.ERROR;
+        }
+        VerifyCredentialResponse response = VerifyCredentialResponse.fromGateKeeperResponse(
+                getGateKeeperService().verifyChallenge(data.userId, 0 /* challenge */,
+                        storedHash.hash, credential.getBytes()));
+        if (progressCallback != null
+                && response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+            progressCallback.onCredentialVerified();
+        }
+        return response;
+    }
+
     @Override
     public VerifyCredentialResponse verifyTiedProfileChallenge(String credential, int type,
             long challenge, int userId) throws RemoteException {
@@ -1521,7 +1586,11 @@
                 unlockUser(userId, fakeToken, fakeToken);
 
                 // migrate credential to GateKeeper
-                setLockCredentialInternal(credential, storedHash.type, null, userId);
+                setLockCredentialInternal(credential, storedHash.type, null,
+                        storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
+                                ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+                                : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
+                                /* TODO(roosa): keep the same password quality */, userId);
                 if (!hasChallenge) {
                     notifyActivePasswordMetricsAvailable(credential, userId);
                     return VerifyCredentialResponse.OK;
@@ -1557,15 +1626,21 @@
                         (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
                 trustManager.setDeviceLockedForUser(userId, false);
             }
+            int reEnrollQuality = storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
+                    ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+                    : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
+                    /* TODO(roosa): keep the same password quality */;
             if (shouldReEnroll) {
-                setLockCredentialInternal(credential, storedHash.type, credential, userId);
+                setLockCredentialInternal(credential, storedHash.type, credential,
+                        reEnrollQuality, userId);
             } else {
                 // Now that we've cleared of all required GK migration, let's do the final
                 // migration to synthetic password.
                 synchronized (mSpManager) {
                     if (shouldMigrateToSyntheticPasswordLocked(userId)) {
                         AuthenticationToken auth = initializeSyntheticPasswordLocked(
-                                storedHash.hash, credential, storedHash.type, userId);
+                                storedHash.hash, credential, storedHash.type, reEnrollQuality,
+                                userId);
                         activateEscrowTokens(auth, userId);
                     }
                 }
@@ -1857,7 +1932,8 @@
      *     FOR THE FIRST TIME on a SP-enabled device. New credential and new SID will be created
      */
     private AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash,
-            String credential, int credentialType, int userId) throws RemoteException {
+            String credential, int credentialType, int requestedQuality,
+            int userId) throws RemoteException {
         Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
         AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid(getGateKeeperService(),
                 credentialHash, credential, userId);
@@ -1866,7 +1942,7 @@
             return null;
         }
         long handle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(),
-                credential, credentialType, auth, userId);
+                credential, credentialType, auth, requestedQuality, userId);
         if (credential != null) {
             if (credentialHash == null) {
                 // Since when initializing SP, we didn't provide an existing password handle
@@ -1895,6 +1971,10 @@
     }
 
     private boolean isSyntheticPasswordBasedCredentialLocked(int userId) throws RemoteException {
+        if (userId == USER_FRP) {
+            final int type = mStorage.readPersistentDataBlock().type;
+            return type == PersistentData.TYPE_SP || type == PersistentData.TYPE_SP_WEAVER;
+        }
         long handle = getSyntheticPasswordHandleLocked(userId);
         // This is a global setting
         long enabled = getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM);
@@ -1919,6 +1999,11 @@
         if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
             userCredential = null;
         }
+        if (userId == USER_FRP) {
+            return mSpManager.verifyFrpCredential(getGateKeeperService(),
+                    userCredential, credentialType, progressCallback);
+        }
+
         long handle = getSyntheticPasswordHandleLocked(userId);
         AuthenticationResult authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
                 getGateKeeperService(), handle, userCredential, userId);
@@ -1969,10 +2054,10 @@
      * added back when new password is set in future.
      */
     private long setLockCredentialWithAuthTokenLocked(String credential, int credentialType,
-            AuthenticationToken auth, int userId) throws RemoteException {
+            AuthenticationToken auth, int requestedQuality, int userId) throws RemoteException {
         if (DEBUG) Slog.d(TAG, "setLockCredentialWithAuthTokenLocked: user=" + userId);
         long newHandle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(),
-                credential, credentialType, auth, userId);
+                credential, credentialType, auth, requestedQuality, userId);
         final Map<Integer, String> profilePasswords;
         if (credential != null) {
             // // not needed by synchronizeUnifiedWorkChallengeForProfiles()
@@ -2014,7 +2099,7 @@
     }
 
     private void spBasedSetLockCredentialInternalLocked(String credential, int credentialType,
-            String savedCredential, int userId) throws RemoteException {
+            String savedCredential, int requestedQuality, int userId) throws RemoteException {
         if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
         if (isManagedProfileWithUnifiedLock(userId)) {
             // get credential from keystore when managed profile has unified lock
@@ -2037,7 +2122,8 @@
         if (auth != null) {
             // We are performing a trusted credential change i.e. a correct existing credential
             // is provided
-            setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, userId);
+            setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, requestedQuality,
+                    userId);
             mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
         } else if (response != null
                 && response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR){
@@ -2046,7 +2132,8 @@
             // Still support this for now but this flow will be removed in the next release.
 
             Slog.w(TAG, "Untrusted credential change invoked");
-            initializeSyntheticPasswordLocked(null, credential, credentialType, userId);
+            initializeSyntheticPasswordLocked(null, credential, credentialType, requestedQuality,
+                    userId);
             synchronizeUnifiedWorkChallengeForProfiles(userId, null);
             mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
         } else /* response == null || responseCode == VerifyCredentialResponse.RESPONSE_RETRY */ {
@@ -2070,7 +2157,8 @@
             if (!isUserSecure(userId)) {
                 if (shouldMigrateToSyntheticPasswordLocked(userId)) {
                     auth = initializeSyntheticPasswordLocked(null, null,
-                            LockPatternUtils.CREDENTIAL_TYPE_NONE, userId);
+                            LockPatternUtils.CREDENTIAL_TYPE_NONE,
+                            DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId);
                 } else /* isSyntheticPasswordBasedCredentialLocked(userId) */ {
                     long pwdHandle = getSyntheticPasswordHandleLocked(userId);
                     auth = mSpManager.unwrapPasswordBasedSyntheticPassword(getGateKeeperService(),
@@ -2132,7 +2220,7 @@
 
     @Override
     public boolean setLockCredentialWithToken(String credential, int type, long tokenHandle,
-            byte[] token, int userId) throws RemoteException {
+            byte[] token, int requestedQuality, int userId) throws RemoteException {
         ensureCallerSystemUid();
         boolean result;
         synchronized (mSpManager) {
@@ -2140,7 +2228,7 @@
                 throw new SecurityException("Escrow token is disabled on the current user");
             }
             result = setLockCredentialWithTokenInternal(credential, type, tokenHandle, token,
-                    userId);
+                    requestedQuality, userId);
         }
         if (result) {
             synchronized (mSeparateChallengeLock) {
@@ -2152,7 +2240,7 @@
     }
 
     private boolean setLockCredentialWithTokenInternal(String credential, int type,
-            long tokenHandle, byte[] token, int userId) throws RemoteException {
+            long tokenHandle, byte[] token, int requestedQuality, int userId) throws RemoteException {
         synchronized (mSpManager) {
             AuthenticationResult result = mSpManager.unwrapTokenBasedSyntheticPassword(
                     getGateKeeperService(), tokenHandle, token, userId);
@@ -2161,7 +2249,8 @@
                 return false;
             }
             long oldHandle = getSyntheticPasswordHandleLocked(userId);
-            setLockCredentialWithAuthTokenLocked(credential, type, result.authToken, userId);
+            setLockCredentialWithAuthTokenLocked(credential, type, result.authToken,
+                    requestedQuality, userId);
             mSpManager.destroyPasswordBasedSyntheticPassword(oldHandle, userId);
             return true;
         }
@@ -2261,4 +2350,69 @@
             throw new SecurityException("Only system can call this API.");
         }
     }
+
+    private class DeviceProvisionedObserver extends ContentObserver {
+        private final Uri mDeviceProvisionedUri = Settings.Global.getUriFor(
+                Settings.Global.DEVICE_PROVISIONED);
+
+        private boolean mRegistered;
+
+        public DeviceProvisionedObserver() {
+            super(null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (mDeviceProvisionedUri.equals(uri)) {
+                updateRegistration();
+
+                if (isProvisioned()) {
+                    Slog.i(TAG, "Reporting device setup complete to IGateKeeperService");
+                    reportDeviceSetupComplete();
+                }
+            }
+        }
+
+        public void onSystemReady() {
+            if (frpCredentialEnabled()) {
+                updateRegistration();
+            } else {
+                // If we don't intend to use frpCredentials and we're not provisioned yet, send
+                // deviceSetupComplete immediately, so gatekeeper can discard any lingering
+                // credentials immediately.
+                if (!isProvisioned()) {
+                    Slog.i(TAG, "FRP credential disabled, reporting device setup complete "
+                            + "to Gatekeeper immediately");
+                    reportDeviceSetupComplete();
+                }
+            }
+        }
+
+        private void reportDeviceSetupComplete() {
+            try {
+                getGateKeeperService().reportDeviceSetupComplete();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failure reporting to IGateKeeperService", e);
+            }
+        }
+
+        private void updateRegistration() {
+            boolean register = !isProvisioned();
+            if (register == mRegistered) {
+                return;
+            }
+            if (register) {
+                mContext.getContentResolver().registerContentObserver(mDeviceProvisionedUri,
+                        false, this);
+            } else {
+                mContext.getContentResolver().unregisterContentObserver(this);
+            }
+            mRegistered = register;
+        }
+
+        private boolean isProvisioned() {
+            return Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index d621a68..79372e48 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -18,6 +18,8 @@
 
 import static android.content.Context.USER_SERVICE;
 
+import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.pm.UserInfo;
@@ -25,6 +27,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.os.Environment;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
 import android.util.ArrayMap;
@@ -33,8 +36,15 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LocalServices;
+import com.android.server.PersistentDataBlockManagerInternal;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.RandomAccessFile;
@@ -79,12 +89,18 @@
     private final Cache mCache = new Cache();
     private final Object mFileWriteLock = new Object();
 
+    private PersistentDataBlockManagerInternal mPersistentDataBlockManagerInternal;
+
     @VisibleForTesting
     public static class CredentialHash {
         static final int VERSION_LEGACY = 0;
         static final int VERSION_GATEKEEPER = 1;
 
         private CredentialHash(byte[] hash, int type, int version) {
+            this(hash, type, version, false /* isBaseZeroPattern */);
+        }
+
+        private CredentialHash(byte[] hash, int type, int version, boolean isBaseZeroPattern) {
             if (type != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
                 if (hash == null) {
                     throw new RuntimeException("Empty hash for CredentialHash");
@@ -97,14 +113,12 @@
             this.hash = hash;
             this.type = type;
             this.version = version;
-            this.isBaseZeroPattern = false;
+            this.isBaseZeroPattern = isBaseZeroPattern;
         }
 
-        private CredentialHash(byte[] hash, boolean isBaseZeroPattern) {
-            this.hash = hash;
-            this.type = LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
-            this.version = VERSION_GATEKEEPER;
-            this.isBaseZeroPattern = isBaseZeroPattern;
+        private static CredentialHash createBaseZeroPattern(byte[] hash) {
+            return new CredentialHash(hash, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+                    VERSION_GATEKEEPER, true /* isBaseZeroPattern */);
         }
 
         static CredentialHash create(byte[] hash, int type) {
@@ -123,6 +137,44 @@
         int type;
         int version;
         boolean isBaseZeroPattern;
+
+        public byte[] toBytes() {
+            Preconditions.checkState(!isBaseZeroPattern, "base zero patterns are not serializable");
+
+            try {
+                ByteArrayOutputStream os = new ByteArrayOutputStream();
+                DataOutputStream dos = new DataOutputStream(os);
+                dos.write(version);
+                dos.write(type);
+                if (hash != null && hash.length > 0) {
+                    dos.writeInt(hash.length);
+                    dos.write(hash);
+                } else {
+                    dos.writeInt(0);
+                }
+                dos.close();
+                return os.toByteArray();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public static CredentialHash fromBytes(byte[] bytes) {
+            try {
+                DataInputStream is = new DataInputStream(new ByteArrayInputStream(bytes));
+                int version = is.read();
+                int type = is.read();
+                int hashSize = is.readInt();
+                byte[] hash = null;
+                if (hashSize > 0) {
+                    hash = new byte[hashSize];
+                    is.readFully(hash);
+                }
+                return new CredentialHash(hash, type, version);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
     }
 
     public LockSettingsStorage(Context context) {
@@ -234,7 +286,7 @@
 
         stored = readFile(getBaseZeroLockPatternFilename(userId));
         if (!ArrayUtils.isEmpty(stored)) {
-            return new CredentialHash(stored, true);
+            return CredentialHash.createBaseZeroPattern(stored);
         }
 
         stored = readFile(getLegacyLockPatternFilename(userId));
@@ -551,6 +603,108 @@
         mCache.clear();
     }
 
+    @Nullable
+    public PersistentDataBlockManagerInternal getPersistentDataBlock() {
+        if (mPersistentDataBlockManagerInternal == null) {
+            mPersistentDataBlockManagerInternal =
+                    LocalServices.getService(PersistentDataBlockManagerInternal.class);
+        }
+        return mPersistentDataBlockManagerInternal;
+    }
+
+    public void writePersistentDataBlock(int persistentType, int userId, int qualityForUi,
+            byte[] payload) {
+        PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlock();
+        if (persistentDataBlock == null) {
+            return;
+        }
+        persistentDataBlock.setFrpCredentialHandle(PersistentData.toBytes(
+                persistentType, userId, qualityForUi, payload));
+    }
+
+    public PersistentData readPersistentDataBlock() {
+        PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlock();
+        if (persistentDataBlock == null) {
+            return PersistentData.NONE;
+        }
+        return PersistentData.fromBytes(persistentDataBlock.getFrpCredentialHandle());
+    }
+
+    public static class PersistentData {
+        static final byte VERSION_1 = 1;
+        static final int VERSION_1_HEADER_SIZE = 1 + 1 + 4 + 4;
+
+        public static final int TYPE_NONE = 0;
+        public static final int TYPE_GATEKEEPER = 1;
+        public static final int TYPE_SP = 2;
+        public static final int TYPE_SP_WEAVER = 3;
+
+        public static final PersistentData NONE = new PersistentData(TYPE_NONE,
+                UserHandle.USER_NULL, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, null);
+
+        final int type;
+        final int userId;
+        final int qualityForUi;
+        final byte[] payload;
+
+        private PersistentData(int type, int userId, int qualityForUi, byte[] payload) {
+            this.type = type;
+            this.userId = userId;
+            this.qualityForUi = qualityForUi;
+            this.payload = payload;
+        }
+
+        public static PersistentData fromBytes(byte[] frpData) {
+            if (frpData == null || frpData.length == 0) {
+                return NONE;
+            }
+
+            DataInputStream is = new DataInputStream(new ByteArrayInputStream(frpData));
+            try {
+                byte version = is.readByte();
+                if (version == PersistentData.VERSION_1) {
+                    int type = is.readByte() & 0xFF;
+                    int userId = is.readInt();
+                    int qualityForUi = is.readInt();
+                    byte[] payload = new byte[frpData.length - VERSION_1_HEADER_SIZE];
+                    System.arraycopy(frpData, VERSION_1_HEADER_SIZE, payload, 0, payload.length);
+                    return new PersistentData(type, userId, qualityForUi, payload);
+                } else {
+                    Slog.wtf(TAG, "Unknown PersistentData version code: " + version);
+                    return null;
+                }
+            } catch (IOException e) {
+                Slog.wtf(TAG, "Could not parse PersistentData", e);
+                return null;
+            }
+        }
+
+        public static byte[] toBytes(int persistentType, int userId, int qualityForUi,
+                byte[] payload) {
+            if (persistentType == PersistentData.TYPE_NONE) {
+                Preconditions.checkArgument(payload == null,
+                        "TYPE_NONE must have empty payload");
+                return null;
+            }
+            Preconditions.checkArgument(payload != null && payload.length > 0,
+                    "empty payload must only be used with TYPE_NONE");
+
+            ByteArrayOutputStream os = new ByteArrayOutputStream(
+                    VERSION_1_HEADER_SIZE + payload.length);
+            DataOutputStream dos = new DataOutputStream(os);
+            try {
+                dos.writeByte(PersistentData.VERSION_1);
+                dos.writeByte(persistentType);
+                dos.writeInt(userId);
+                dos.writeInt(qualityForUi);
+                dos.write(payload);
+            } catch (IOException e) {
+                throw new RuntimeException("ByteArrayOutputStream cannot throw IOException");
+            }
+            return os.toByteArray();
+        }
+    }
+
     public interface Callback {
         void initialize(SQLiteDatabase db);
     }
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 2f8a1b4..f45c208 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -18,13 +18,15 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
 import android.hardware.weaver.V1_0.IWeaver;
 import android.hardware.weaver.V1_0.WeaverConfig;
 import android.hardware.weaver.V1_0.WeaverReadResponse;
 import android.hardware.weaver.V1_0.WeaverReadStatus;
 import android.hardware.weaver.V1_0.WeaverStatus;
-import android.os.RemoteException;
 import android.security.GateKeeper;
+import android.os.RemoteException;
+import android.os.UserManager;
 import android.service.gatekeeper.GateKeeperResponse;
 import android.service.gatekeeper.IGateKeeperService;
 import android.util.ArrayMap;
@@ -33,8 +35,10 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.widget.ICheckCredentialProgressCallback;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.server.locksettings.LockSettingsStorage.PersistentData;
 
 import libcore.util.HexEncoding;
 
@@ -253,8 +257,11 @@
     private IWeaver mWeaver;
     private WeaverConfig mWeaverConfig;
 
-    public SyntheticPasswordManager(LockSettingsStorage storage) {
+    private final UserManager mUserManager;
+
+    public SyntheticPasswordManager(LockSettingsStorage storage, UserManager userManager) {
         mStorage = storage;
+        mUserManager = userManager;
     }
 
     @VisibleForTesting
@@ -557,7 +564,8 @@
      * @see #clearSidForUser
      */
     public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
-            String credential, int credentialType, AuthenticationToken authToken, int userId)
+            String credential, int credentialType, AuthenticationToken authToken,
+            int requestedQuality, int userId)
                     throws RemoteException {
         if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
             credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
@@ -579,6 +587,7 @@
                 return DEFAULT_HANDLE;
             }
             saveWeaverSlot(weaverSlot, handle, userId);
+            synchronizeWeaverFrpPassword(pwd, requestedQuality, userId, weaverSlot);
 
             pwd.passwordHandle = null;
             sid = GateKeeper.INVALID_SECURE_USER_ID;
@@ -598,6 +607,7 @@
             sid = sidFromPasswordHandle(pwd.passwordHandle);
             applicationId = transformUnderSecdiscardable(pwdToken,
                     createSecdiscardable(handle, userId));
+            synchronizeFrpPassword(pwd, requestedQuality, userId);
         }
         saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
 
@@ -606,6 +616,57 @@
         return handle;
     }
 
+    public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
+            String userCredential, int credentialType,
+            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
+        PersistentData persistentData = mStorage.readPersistentDataBlock();
+        if (persistentData.type == PersistentData.TYPE_SP) {
+            PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
+            byte[] pwdToken = computePasswordToken(userCredential, pwd);
+
+            GateKeeperResponse response = gatekeeper.verify(fakeUid(persistentData.userId),
+                    pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
+            return VerifyCredentialResponse.fromGateKeeperResponse(response);
+        } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
+            PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
+            byte[] pwdToken = computePasswordToken(userCredential, pwd);
+            int weaverSlot = persistentData.userId;
+
+            return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
+        } else {
+            Log.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
+                    + persistentData.type);
+            return VerifyCredentialResponse.ERROR;
+        }
+    }
+
+
+    private void synchronizeFrpPassword(PasswordData pwd,
+            int requestedQuality, int userId) {
+        if (mStorage.getPersistentDataBlock() != null
+                && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
+            if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
+                mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality,
+                        pwd.toBytes());
+            } else {
+                mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null);
+            }
+        }
+    }
+
+    private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId,
+            int weaverSlot) {
+        if (mStorage.getPersistentDataBlock() != null
+                && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
+            if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
+                mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot,
+                        requestedQuality, pwd.toBytes());
+            } else {
+                mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, 0, 0, null);
+            }
+        }
+    }
+
     private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
 
     public long createTokenBasedSyntheticPassword(byte[] token, int userId) {
@@ -730,6 +791,12 @@
                     if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
                         pwd.passwordHandle = reenrollResponse.getPayload();
                         saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
+                        synchronizeFrpPassword(pwd,
+                                pwd.passwordType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN
+                                ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+                                : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
+                                /* TODO(roosa): keep the same password quality */,
+                                userId);
                     } else {
                         Log.w(TAG, "Fail to re-enroll user password for user " + userId);
                         // continue the flow anyway
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index e746355..ab685ca 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -18,7 +18,6 @@
 
 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
-import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.MODIFY_NETWORK_ACCOUNTING;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.content.Intent.ACTION_SHUTDOWN;
@@ -31,7 +30,6 @@
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.SET_DEFAULT;
 import static android.net.NetworkStats.SET_FOREGROUND;
-import static android.net.NetworkStats.TAG_ALL;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
@@ -58,14 +56,13 @@
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
-import static com.android.internal.util.Preconditions.checkArgument;
+
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
 import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
 import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
 
 import android.app.AlarmManager;
-import android.app.IAlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -1041,7 +1038,7 @@
         // snapshot and record current counters; read UID stats first to
         // avoid over counting dev stats.
         final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
-        final NetworkStats xtSnapshot = getNetworkStatsXtAndVt();
+        final NetworkStats xtSnapshot = getNetworkStatsXt();
         final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
 
 
@@ -1367,7 +1364,8 @@
 
     /**
      * Return snapshot of current UID statistics, including any
-     * {@link TrafficStats#UID_TETHERING} and {@link #mUidOperations} values.
+     * {@link TrafficStats#UID_TETHERING}, video calling data usage, and {@link #mUidOperations}
+     * values.
      */
     private NetworkStats getNetworkStatsUidDetail() throws RemoteException {
         final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
@@ -1375,43 +1373,34 @@
         // fold tethering stats and operations into uid snapshot
         final NetworkStats tetherSnapshot = getNetworkStatsTethering();
         uidSnapshot.combineAllValues(tetherSnapshot);
+
+        final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
+                Context.TELEPHONY_SERVICE);
+
+        // fold video calling data usage stats into uid snapshot
+        final NetworkStats vtStats = telephonyManager.getVtDataUsage(true);
+        if (vtStats != null) {
+            uidSnapshot.combineAllValues(vtStats);
+        }
         uidSnapshot.combineAllValues(mUidOperations);
 
         return uidSnapshot;
     }
 
     /**
-     * Return snapshot of current XT plus VT statistics.
+     * Return snapshot of current XT statistics with video calling data usage statistics.
      */
-    private NetworkStats getNetworkStatsXtAndVt() throws RemoteException {
+    private NetworkStats getNetworkStatsXt() throws RemoteException {
         final NetworkStats xtSnapshot = mNetworkManager.getNetworkStatsSummaryXt();
 
-        TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
+        final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
                 Context.TELEPHONY_SERVICE);
 
-        long usage = tm.getVtDataUsage();
-
-        if (LOGV) Slog.d(TAG, "VT call data usage = " + usage);
-
-        final NetworkStats vtSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 1);
-
-        final NetworkStats.Entry entry = new NetworkStats.Entry();
-        entry.iface = VT_INTERFACE;
-        entry.uid = -1;
-        entry.set = TAG_ALL;
-        entry.tag = TAG_NONE;
-
-        // Since modem only tell us the total usage instead of each usage for RX and TX,
-        // we need to split it up (though it might not quite accurate). At
-        // least we can make sure the data usage report to the user will still be accurate.
-        entry.rxBytes = usage / 2;
-        entry.rxPackets = 0;
-        entry.txBytes = usage - entry.rxBytes;
-        entry.txPackets = 0;
-        vtSnapshot.combineValues(entry);
-
-        // Merge VT int XT
-        xtSnapshot.combineAllValues(vtSnapshot);
+        // Merge video calling data usage into XT
+        final NetworkStats vtSnapshot = telephonyManager.getVtDataUsage(false);
+        if (vtSnapshot != null) {
+            xtSnapshot.combineAllValues(vtSnapshot);
+        }
 
         return xtSnapshot;
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index baa7837..b630368 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3225,7 +3225,7 @@
                 Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, defaultWarningEnabled) != 0;
         if (warningEnabled) {
             Toast toast = Toast.makeText(getContext(), mHandler.getLooper(), toastText,
-                    Toast.LENGTH_LONG);
+                    Toast.LENGTH_SHORT);
             toast.show();
         }
     }
@@ -3759,13 +3759,7 @@
             return true;
         }
         if (record.sbn.isGroup()) {
-            if (notification.isGroupSummary()
-                    && notification.getGroupAlertBehavior() == Notification.GROUP_ALERT_CHILDREN) {
-                return true;
-            } else if (notification.isGroupChild()
-                    && notification.getGroupAlertBehavior() == Notification.GROUP_ALERT_SUMMARY) {
-                return true;
-            }
+            return notification.suppressAlertingDueToGrouping();
         }
         return false;
     }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 4c3efce..2c0cc95 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -447,7 +447,9 @@
                 boolean isGroupSummary = record.getNotification().isGroupSummary();
                 record.setGlobalSortKey(
                         String.format("intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x",
-                        record.isRecentlyIntrusive() ? '0' : '1',
+                        record.isRecentlyIntrusive()
+                                && record.getImportance() > NotificationManager.IMPORTANCE_MIN
+                                ? '0' : '1',
                         groupProxy.getAuthoritativeRank(),
                         isGroupSummary ? '0' : '1',
                         groupSortKeyPortion,
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 99c025d..5c4c040 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -405,10 +405,11 @@
         }
     }
 
-    public void freeCache(String uuid, long freeStorageSize, int flags) throws InstallerException {
+    public void freeCache(String uuid, long targetFreeBytes, long cacheReservedBytes, int flags)
+            throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
-            mInstalld.freeCache(uuid, freeStorageSize, flags);
+            mInstalld.freeCache(uuid, targetFreeBytes, cacheReservedBytes, flags);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index b165984..211a1c9 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -21,6 +21,7 @@
 import android.annotation.UserIdInt;
 import android.content.Intent;
 import android.content.pm.InstantAppInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -32,6 +33,8 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
@@ -76,7 +79,16 @@
 
     private static final String LOG_TAG = "InstantAppRegistry";
 
-    private static final long DEFAULT_UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS =
+    static final long DEFAULT_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
+            DEBUG ? 30 * 1000L /* thirty seconds */ : 7 * 24 * 60 * 60 * 1000L; /* one week */
+
+    private static final long DEFAULT_INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
+            DEBUG ? 60 * 1000L /* one min */ : 6 * 30 * 24 * 60 * 60 * 1000L; /* six months */
+
+    static final long DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
+            DEBUG ? 30 * 1000L /* thirty seconds */ : 7 * 24 * 60 * 60 * 1000L; /* one week */
+
+    private static final long DEFAULT_UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
             DEBUG ? 60 * 1000L /* one min */ : 6 * 30 * 24 * 60 * 60 * 1000L; /* six months */
 
     private static final String INSTANT_APPS_FOLDER = "instant";
@@ -535,46 +547,195 @@
         }
     }
 
-    public void pruneInstantAppsLPw() {
-        // For now we prune only state for uninstalled instant apps
-        final long maxCacheDurationMillis = Settings.Global.getLong(
+    void pruneInstantApps() {
+        final long maxInstalledCacheDuration = Settings.Global.getLong(
                 mService.mContext.getContentResolver(),
-                Settings.Global.UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS,
-                DEFAULT_UNINSTALLED_INSTANT_APP_CACHE_DURATION_MILLIS);
+                Settings.Global.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
+                DEFAULT_INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
 
-        for (int userId : UserManagerService.getInstance().getUserIds()) {
-            // Prune in-memory state
-            removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) -> {
-                final long elapsedCachingMillis = System.currentTimeMillis() - state.mTimestamp;
-                return (elapsedCachingMillis > maxCacheDurationMillis);
-            }, userId);
+        final long maxUninstalledCacheDuration = Settings.Global.getLong(
+                mService.mContext.getContentResolver(),
+                Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
+                DEFAULT_UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
 
-            // Prune on-disk state
-            File instantAppsDir = getInstantApplicationsDir(userId);
-            if (!instantAppsDir.exists()) {
-                continue;
-            }
-            File[] files = instantAppsDir.listFiles();
-            if (files == null) {
-                continue;
-            }
-            for (File instantDir : files) {
-                if (!instantDir.isDirectory()) {
+        try {
+            pruneInstantApps(Long.MAX_VALUE,
+                    maxInstalledCacheDuration, maxUninstalledCacheDuration);
+        } catch (IOException e) {
+            Slog.e(LOG_TAG, "Error pruning installed and uninstalled instant apps", e);
+        }
+    }
+
+    boolean pruneInstalledInstantApps(long neededSpace, long maxInstalledCacheDuration) {
+        try {
+            return pruneInstantApps(neededSpace, maxInstalledCacheDuration, Long.MAX_VALUE);
+        } catch (IOException e) {
+            Slog.e(LOG_TAG, "Error pruning installed instant apps", e);
+            return false;
+        }
+    }
+
+    boolean pruneUninstalledInstantApps(long neededSpace, long maxUninstalledCacheDuration) {
+        try {
+            return pruneInstantApps(neededSpace, Long.MAX_VALUE, maxUninstalledCacheDuration);
+        } catch (IOException e) {
+            Slog.e(LOG_TAG, "Error pruning uninstalled instant apps", e);
+            return false;
+        }
+    }
+
+    /**
+     * Prunes instant apps until there is enough <code>neededSpace</code>. Both
+     * installed and uninstalled instant apps are pruned that are older than
+     * <code>maxInstalledCacheDuration</code> and <code>maxUninstalledCacheDuration</code>
+     * respectively. All times are in milliseconds.
+     *
+     * @param neededSpace The space to ensure is free.
+     * @param maxInstalledCacheDuration The max duration for caching installed apps in millis.
+     * @param maxUninstalledCacheDuration The max duration for caching uninstalled apps in millis.
+     * @return Whether enough space was freed.
+     *
+     * @throws IOException
+     */
+    private boolean pruneInstantApps(long neededSpace, long maxInstalledCacheDuration,
+            long maxUninstalledCacheDuration) throws IOException {
+        final StorageManager storage = mService.mContext.getSystemService(StorageManager.class);
+        final File file = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
+
+        if (file.getUsableSpace() >= neededSpace) {
+            return true;
+        }
+
+        List<String> packagesToDelete = null;
+
+        final int[] allUsers;
+        final long now = System.currentTimeMillis();
+
+        // Prune first installed instant apps
+        synchronized (mService.mPackages) {
+            allUsers = PackageManagerService.sUserManager.getUserIds();
+
+            final int packageCount = mService.mPackages.size();
+            for (int i = 0; i < packageCount; i++) {
+                final PackageParser.Package pkg = mService.mPackages.valueAt(i);
+                if (now - pkg.getLatestPackageUseTimeInMills() < maxInstalledCacheDuration) {
                     continue;
                 }
-
-                File metadataFile = new File(instantDir, INSTANT_APP_METADATA_FILE);
-                if (!metadataFile.exists()) {
+                if (!(pkg.mExtras instanceof PackageSetting)) {
                     continue;
                 }
+                final PackageSetting  ps = (PackageSetting) pkg.mExtras;
+                boolean installedOnlyAsInstantApp = false;
+                for (int userId : allUsers) {
+                    if (ps.getInstalled(userId)) {
+                        if (ps.getInstantApp(userId)) {
+                            installedOnlyAsInstantApp = true;
+                        } else {
+                            installedOnlyAsInstantApp = false;
+                            break;
+                        }
+                    }
+                }
+                if (installedOnlyAsInstantApp) {
+                    if (packagesToDelete == null) {
+                        packagesToDelete = new ArrayList<>();
+                    }
+                    packagesToDelete.add(pkg.packageName);
+                }
+            }
 
-                final long elapsedCachingMillis = System.currentTimeMillis()
-                        - metadataFile.lastModified();
-                if (elapsedCachingMillis > maxCacheDurationMillis) {
-                    deleteDir(instantDir);
+            if (packagesToDelete != null) {
+                packagesToDelete.sort((String lhs, String rhs) -> {
+                    final PackageParser.Package lhsPkg = mService.mPackages.get(lhs);
+                    final PackageParser.Package rhsPkg = mService.mPackages.get(rhs);
+                    if (lhsPkg == null && rhsPkg == null) {
+                        return 0;
+                    } else if (lhsPkg == null) {
+                        return -1;
+                    } else if (rhsPkg == null) {
+                        return 1;
+                    } else {
+                        if (lhsPkg.getLatestPackageUseTimeInMills() >
+                                rhsPkg.getLatestPackageUseTimeInMills()) {
+                            return 1;
+                        } else if (lhsPkg.getLatestPackageUseTimeInMills() <
+                                rhsPkg.getLatestPackageUseTimeInMills()) {
+                            return -1;
+                        } else {
+                            if (lhsPkg.mExtras instanceof PackageSetting
+                                    && rhsPkg.mExtras instanceof PackageSetting) {
+                                final PackageSetting lhsPs = (PackageSetting) lhsPkg.mExtras;
+                                final PackageSetting rhsPs = (PackageSetting) rhsPkg.mExtras;
+                                if (lhsPs.firstInstallTime > rhsPs.firstInstallTime) {
+                                    return 1;
+                                } else {
+                                    return -1;
+                                }
+                            } else {
+                                return 0;
+                            }
+                        }
+                    }
+                });
+            }
+        }
+
+        if (packagesToDelete != null) {
+            final int packageCount = packagesToDelete.size();
+            for (int i = 0; i < packageCount; i++) {
+                final String packageToDelete = packagesToDelete.get(i);
+                if (mService.deletePackageX(packageToDelete, PackageManager.VERSION_CODE_HIGHEST,
+                        UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS)
+                                == PackageManager.DELETE_SUCCEEDED) {
+                    if (file.getUsableSpace() >= neededSpace) {
+                        return true;
+                    }
                 }
             }
         }
+
+        // Prune uninstalled instant apps
+        synchronized (mService.mPackages) {
+            // TODO: Track last used time for uninstalled instant apps for better pruning
+            for (int userId : UserManagerService.getInstance().getUserIds()) {
+                // Prune in-memory state
+                removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) -> {
+                    final long elapsedCachingMillis = System.currentTimeMillis() - state.mTimestamp;
+                    return (elapsedCachingMillis > maxUninstalledCacheDuration);
+                }, userId);
+
+                // Prune on-disk state
+                File instantAppsDir = getInstantApplicationsDir(userId);
+                if (!instantAppsDir.exists()) {
+                    continue;
+                }
+                File[] files = instantAppsDir.listFiles();
+                if (files == null) {
+                    continue;
+                }
+                for (File instantDir : files) {
+                    if (!instantDir.isDirectory()) {
+                        continue;
+                    }
+
+                    File metadataFile = new File(instantDir, INSTANT_APP_METADATA_FILE);
+                    if (!metadataFile.exists()) {
+                        continue;
+                    }
+
+                    final long elapsedCachingMillis = System.currentTimeMillis()
+                            - metadataFile.lastModified();
+                    if (elapsedCachingMillis > maxUninstalledCacheDuration) {
+                        deleteDir(instantDir);
+                        if (file.getUsableSpace() >= neededSpace) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+
+        return false;
     }
 
     private @Nullable List<InstantAppInfo> getInstalledInstantApplicationsLPr(
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cc6d51b..3ca68de 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -877,9 +877,9 @@
             new ParallelPackageParserCallback();
 
     public static final class SharedLibraryEntry {
-        public final String path;
-        public final String apk;
-        public final SharedLibraryInfo info;
+        public final @Nullable String path;
+        public final @Nullable String apk;
+        public final @NonNull SharedLibraryInfo info;
 
         SharedLibraryEntry(String _path, String _apk, String name, int version, int type,
                 String declaringPackageName, int declaringPackageVersionCode) {
@@ -1314,6 +1314,9 @@
     // Delay time in millisecs
     static final int BROADCAST_DELAY = 10 * 1000;
 
+    private static final long DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD =
+            2 * 60 * 60 * 1000L; /* two hours */
+
     static UserManagerService sUserManager;
 
     // Stores a list of users whose package restrictions file needs to be updated
@@ -4156,13 +4159,13 @@
 
     @Override
     public void freeStorageAndNotify(final String volumeUuid, final long freeStorageSize,
-            final IPackageDataObserver observer) {
+            final int storageFlags, final IPackageDataObserver observer) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CLEAR_APP_CACHE, null);
         mHandler.post(() -> {
             boolean success = false;
             try {
-                freeStorage(volumeUuid, freeStorageSize, 0);
+                freeStorage(volumeUuid, freeStorageSize, storageFlags);
                 success = true;
             } catch (IOException e) {
                 Slog.w(TAG, e);
@@ -4179,13 +4182,13 @@
 
     @Override
     public void freeStorage(final String volumeUuid, final long freeStorageSize,
-            final IntentSender pi) {
+            final int storageFlags, final IntentSender pi) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CLEAR_APP_CACHE, TAG);
         mHandler.post(() -> {
             boolean success = false;
             try {
-                freeStorage(volumeUuid, freeStorageSize, 0);
+                freeStorage(volumeUuid, freeStorageSize, storageFlags);
                 success = true;
             } catch (IOException e) {
                 Slog.w(TAG, e);
@@ -4210,10 +4213,14 @@
         if (file.getUsableSpace() >= bytes) return;
 
         if (ENABLE_FREE_CACHE_V2) {
-            final boolean aggressive = (storageFlags
-                    & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0;
             final boolean internalVolume = Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL,
                     volumeUuid);
+            final boolean aggressive = (storageFlags
+                    & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0;
+            final boolean defyReserved = (storageFlags
+                    & StorageManager.FLAG_ALLOCATE_DEFY_RESERVED) != 0;
+            final long reservedBytes = (aggressive || defyReserved) ? 0
+                    : storage.getStorageCacheBytes(file);
 
             // 1. Pre-flight to determine if we have any chance to succeed
             // 2. Consider preloaded data (after 1w honeymoon, unless aggressive)
@@ -4231,29 +4238,52 @@
 
             // 4. Consider cached app data (above quotas)
             try {
-                mInstaller.freeCache(volumeUuid, bytes, Installer.FLAG_FREE_CACHE_V2);
+                mInstaller.freeCache(volumeUuid, bytes, reservedBytes,
+                        Installer.FLAG_FREE_CACHE_V2);
             } catch (InstallerException ignored) {
             }
             if (file.getUsableSpace() >= bytes) return;
 
-            // 5. Consider shared libraries with refcount=0 and age>2h
+            // 5. Consider shared libraries with refcount=0 and age>min cache period
+            if (internalVolume && pruneUnusedStaticSharedLibraries(bytes,
+                    android.provider.Settings.Global.getLong(mContext.getContentResolver(),
+                            Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
+                            DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD))) {
+                return;
+            }
+
             // 6. Consider dexopt output (aggressive only)
-            // 7. Consider ephemeral apps not used in last week
+            // TODO: Implement
+
+            // 7. Consider installed instant apps unused longer than min cache period
+            if (internalVolume && mInstantAppRegistry.pruneInstalledInstantApps(bytes,
+                    android.provider.Settings.Global.getLong(mContext.getContentResolver(),
+                            Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
+                            InstantAppRegistry.DEFAULT_INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) {
+                return;
+            }
 
             // 8. Consider cached app data (below quotas)
             try {
-                mInstaller.freeCache(volumeUuid, bytes, Installer.FLAG_FREE_CACHE_V2
-                        | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+                mInstaller.freeCache(volumeUuid, bytes, reservedBytes,
+                        Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA);
             } catch (InstallerException ignored) {
             }
             if (file.getUsableSpace() >= bytes) return;
 
             // 9. Consider DropBox entries
-            // 10. Consider ephemeral cookies
+            // TODO: Implement
 
+            // 10. Consider instant meta-data (uninstalled apps) older that min cache period
+            if (internalVolume && mInstantAppRegistry.pruneUninstalledInstantApps(bytes,
+                    android.provider.Settings.Global.getLong(mContext.getContentResolver(),
+                            Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
+                            InstantAppRegistry.DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) {
+                return;
+            }
         } else {
             try {
-                mInstaller.freeCache(volumeUuid, bytes, 0);
+                mInstaller.freeCache(volumeUuid, bytes, 0, 0);
             } catch (InstallerException ignored) {
             }
             if (file.getUsableSpace() >= bytes) return;
@@ -4262,6 +4292,69 @@
         throw new IOException("Failed to free " + bytes + " on storage device at " + file);
     }
 
+    private boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod)
+            throws IOException {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
+
+        List<VersionedPackage> packagesToDelete = null;
+        final long now = System.currentTimeMillis();
+
+        synchronized (mPackages) {
+            final int[] allUsers = sUserManager.getUserIds();
+            final int libCount = mSharedLibraries.size();
+            for (int i = 0; i < libCount; i++) {
+                final SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
+                if (versionedLib == null) {
+                    continue;
+                }
+                final int versionCount = versionedLib.size();
+                for (int j = 0; j < versionCount; j++) {
+                    SharedLibraryInfo libInfo = versionedLib.valueAt(j).info;
+                    // Skip packages that are not static shared libs.
+                    if (!libInfo.isStatic()) {
+                        break;
+                    }
+                    // Important: We skip static shared libs used for some user since
+                    // in such a case we need to keep the APK on the device. The check for
+                    // a lib being used for any user is performed by the uninstall call.
+                    final VersionedPackage declaringPackage = libInfo.getDeclaringPackage();
+                    // Resolve the package name - we use synthetic package names internally
+                    final String internalPackageName = resolveInternalPackageNameLPr(
+                            declaringPackage.getPackageName(), declaringPackage.getVersionCode());
+                    final PackageSetting ps = mSettings.getPackageLPr(internalPackageName);
+                    // Skip unused static shared libs cached less than the min period
+                    // to prevent pruning a lib needed by a subsequently installed package.
+                    if (ps == null || now - ps.lastUpdateTime < maxCachePeriod) {
+                        continue;
+                    }
+                    if (packagesToDelete == null) {
+                        packagesToDelete = new ArrayList<>();
+                    }
+                    packagesToDelete.add(new VersionedPackage(internalPackageName,
+                            declaringPackage.getVersionCode()));
+                }
+            }
+        }
+
+        if (packagesToDelete != null) {
+            final int packageCount = packagesToDelete.size();
+            for (int i = 0; i < packageCount; i++) {
+                final VersionedPackage pkgToDelete = packagesToDelete.get(i);
+                // Delete the package synchronously (will fail of the lib used for any user).
+                if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getVersionCode(),
+                        UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS)
+                                == PackageManager.DELETE_SUCCEEDED) {
+                    if (volume.getUsableSpace() >= neededSpace) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
     /**
      * Update given flags based on encryption status of current user.
      */
@@ -10712,8 +10805,7 @@
                     final int versionCount = versionedLib.size();
                     for (int i = 0; i < versionCount; i++) {
                         SharedLibraryInfo libInfo = versionedLib.valueAt(i).info;
-                        // TODO: We will change version code to long, so in the new API it is long
-                        final int libVersionCode = (int) libInfo.getDeclaringPackage()
+                        final int libVersionCode = libInfo.getDeclaringPackage()
                                 .getVersionCode();
                         if (libInfo.getVersion() <  pkg.staticSharedLibVersion) {
                             minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
@@ -15556,7 +15648,7 @@
                             origin.resolvedPath, isForwardLocked(), packageAbiOverride);
 
                     try {
-                        mInstaller.freeCache(null, sizeBytes + lowThreshold, 0);
+                        mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
                         pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
                                 installFlags, packageAbiOverride);
                     } catch (InstallerException e) {
@@ -18559,7 +18651,7 @@
      *  persisting settings for later use
      *  sending a broadcast if necessary
      */
-    private int deletePackageX(String packageName, int versionCode, int userId, int deleteFlags) {
+    int deletePackageX(String packageName, int versionCode, int userId, int deleteFlags) {
         final PackageRemovedInfo info = new PackageRemovedInfo(this);
         final boolean res;
 
@@ -18602,7 +18694,7 @@
                         pkg.staticSharedLibVersion);
                 if (libEntry != null) {
                     for (int currUserId : allUsers) {
-                        if (userId != UserHandle.USER_ALL && userId != currUserId) {
+                        if (removeUser != UserHandle.USER_ALL && removeUser != currUserId) {
                             continue;
                         }
                         List<VersionedPackage> libClientPackages = getPackagesUsingSharedLibraryLPr(
@@ -24348,9 +24440,7 @@
 
         @Override
         public void pruneInstantApps() {
-            synchronized (mPackages) {
-                mInstantAppRegistry.pruneInstantAppsLPw();
-            }
+            mInstantAppRegistry.pruneInstantApps();
         }
 
         @Override
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index cb6bb1e..76d5c33 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -60,7 +60,6 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
@@ -1128,16 +1127,25 @@
                 + ", mOrientationSensorEnabled=" + mOrientationSensorEnabled
                 + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
                 + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
+        final boolean keyguardGoingAway = mWindowManagerInternal.isKeyguardGoingAway();
+
         boolean disable = true;
         // Note: We postpone the rotating of the screen until the keyguard as well as the
-        // window manager have reported a draw complete.
-        if (mScreenOnEarly && mAwake &&
-                mKeyguardDrawComplete && mWindowManagerDrawComplete) {
+        // window manager have reported a draw complete or the keyguard is going away in dismiss
+        // mode.
+        if (mScreenOnEarly && mAwake && ((mKeyguardDrawComplete && mWindowManagerDrawComplete)
+                || keyguardGoingAway)) {
             if (needSensorRunningLp()) {
                 disable = false;
                 //enable listener if not already enabled
                 if (!mOrientationSensorEnabled) {
-                    mOrientationListener.enable();
+                    // Don't clear the current sensor orientation if the keyguard is going away in
+                    // dismiss mode. This allows window manager to use the last sensor reading to
+                    // determine the orientation vs. falling back to the last known orientation if
+                    // the sensor reading was cleared which can cause it to relaunch the app that
+                    // will show in the wrong orientation first before correcting leading to app
+                    // launch delays.
+                    mOrientationListener.enable(!keyguardGoingAway /* clearCurrentRotation */);
                     if(localLOGV) Slog.v(TAG, "Enabling listeners");
                     mOrientationSensorEnabled = true;
                 }
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index 8ef0acb..64f64c0 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -109,24 +109,37 @@
      * {@link #onProposedRotationChanged(int)} when the device orientation changes.
      */
     public void enable() {
+        enable(true /* clearCurrentRotation */);
+    }
+
+    /**
+     * Enables the WindowOrientationListener so it will monitor the sensor and call
+     * {@link #onProposedRotationChanged(int)} when the device orientation changes.
+     *
+     * @param clearCurrentRotation True if the current proposed sensor rotation should be cleared as
+     *                             part of the reset.
+     */
+    public void enable(boolean clearCurrentRotation) {
         synchronized (mLock) {
             if (mSensor == null) {
                 Slog.w(TAG, "Cannot detect sensors. Not enabled");
                 return;
             }
-            if (mEnabled == false) {
-                if (LOG) {
-                    Slog.d(TAG, "WindowOrientationListener enabled");
-                }
-                mOrientationJudge.resetLocked();
-                if (mSensor.getType() == Sensor.TYPE_ACCELEROMETER) {
-                    mSensorManager.registerListener(
-                            mOrientationJudge, mSensor, mRate, DEFAULT_BATCH_LATENCY, mHandler);
-                } else {
-                    mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler);
-                }
-                mEnabled = true;
+            if (mEnabled) {
+                return;
             }
+            if (LOG) {
+                Slog.d(TAG, "WindowOrientationListener enabled clearCurrentRotation="
+                        + clearCurrentRotation);
+            }
+            mOrientationJudge.resetLocked(clearCurrentRotation);
+            if (mSensor.getType() == Sensor.TYPE_ACCELEROMETER) {
+                mSensorManager.registerListener(
+                        mOrientationJudge, mSensor, mRate, DEFAULT_BATCH_LATENCY, mHandler);
+            } else {
+                mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler);
+            }
+            mEnabled = true;
         }
     }
 
@@ -278,8 +291,11 @@
          * Resets the state of the judge.
          *
          * Should only be called when holding WindowOrientationListener lock.
+         *
+         * @param clearCurrentRotation True if the current proposed sensor rotation should be
+         *                             cleared as part of the reset.
          */
-        public abstract void resetLocked();
+        public abstract void resetLocked(boolean clearCurrentRotation);
 
         /**
          * Dumps internal state of the orientation judge.
@@ -602,7 +618,7 @@
                     if (LOG) {
                         Slog.v(TAG, "Resetting orientation listener.");
                     }
-                    resetLocked();
+                    resetLocked(true /* clearCurrentRotation */);
                     skipSample = true;
                 } else {
                     final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS);
@@ -778,9 +794,11 @@
         }
 
         @Override
-        public void resetLocked() {
+        public void resetLocked(boolean clearCurrentRotation) {
             mLastFilteredTimestampNanos = Long.MIN_VALUE;
-            mProposedRotation = -1;
+            if (clearCurrentRotation) {
+                mProposedRotation = -1;
+            }
             mFlatTimestampNanos = Long.MIN_VALUE;
             mFlat = false;
             mSwingTimestampNanos = Long.MIN_VALUE;
@@ -1015,9 +1033,11 @@
         }
 
         @Override
-        public void resetLocked() {
-            mProposedRotation = -1;
-            mDesiredRotation = -1;
+        public void resetLocked(boolean clearCurrentRotation) {
+            if (clearCurrentRotation) {
+                mProposedRotation = -1;
+                mDesiredRotation = -1;
+            }
             mTouching = false;
             mTouchEndedTimestampNanos = Long.MIN_VALUE;
             unscheduleRotationEvaluationLocked();
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 1b4eaf5..da90e5a 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -30,7 +30,7 @@
  */
 public class KeyguardServiceDelegate {
     private static final String TAG = "KeyguardServiceDelegate";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private static final int SCREEN_STATE_OFF = 0;
     private static final int SCREEN_STATE_TURNING_ON = 1;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 7ffcf1c..a94484a 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -87,6 +87,7 @@
 import com.android.server.RescueParty;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
+import com.android.server.UiThread;
 import com.android.server.Watchdog;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.lights.Light;
@@ -1943,7 +1944,8 @@
                         && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
                     nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
                     if (now < nextTimeout) {
-                        if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT) {
+                        if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT
+                                || mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) {
                             mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
                         } else if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
                             mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
@@ -2731,9 +2733,9 @@
         };
 
         // ShutdownThread must run on a looper capable of displaying the UI.
-        Message msg = Message.obtain(mHandler, runnable);
+        Message msg = Message.obtain(UiThread.getHandler(), runnable);
         msg.setAsynchronous(true);
-        mHandler.sendMessage(msg);
+        UiThread.getHandler().sendMessage(msg);
 
         // PowerManager.reboot() is documented not to return so just wait for the inevitable.
         if (wait) {
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index 69ddc93..88b6d87 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -16,71 +16,60 @@
 
 package com.android.server.storage;
 
-import android.app.NotificationChannel;
-
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.util.DumpUtils;
-import com.android.server.EventLogTags;
-import com.android.server.SystemService;
-import com.android.server.pm.InstructionSets;
+import android.annotation.WorkerThread;
 import android.app.Notification;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.IPackageDataObserver;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.net.TrafficStats;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.FileObserver;
 import android.os.Handler;
 import android.os.Message;
-import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.ShellCommand;
-import android.os.StatFs;
-import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
-import android.provider.Settings;
-import android.text.format.Formatter;
-import android.util.EventLog;
+import android.os.storage.VolumeInfo;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
 import android.util.Slog;
-import android.util.TimeUtils;
 
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.concurrent.atomic.AtomicInteger;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.EventLogTags;
+import com.android.server.IoThread;
+import com.android.server.SystemService;
+import com.android.server.pm.InstructionSets;
+import com.android.server.pm.PackageManagerService;
 
 import dalvik.system.VMRuntime;
 
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
 /**
- * This class implements a service to monitor the amount of disk
- * storage space on the device.  If the free storage on device is less
- * than a tunable threshold value (a secure settings parameter;
- * default 10%) a low memory notification is displayed to alert the
- * user. If the user clicks on the low memory notification the
- * Application Manager application gets launched to let the user free
- * storage space.
- *
- * Event log events: A low memory event with the free storage on
- * device in bytes is logged to the event log when the device goes low
- * on storage space.  The amount of free storage on the device is
- * periodically logged to the event log. The log interval is a secure
- * settings parameter with a default value of 12 hours.  When the free
- * storage differential goes below a threshold (again a secure
- * settings parameter with a default value of 2MB), the free memory is
- * logged to the event log.
+ * Service that monitors and maintains free space on storage volumes.
+ * <p>
+ * As the free space on a volume nears the threshold defined by
+ * {@link StorageManager#getStorageLowBytes(File)}, this service will clear out
+ * cached data to keep the disk from entering this low state.
  */
 public class DeviceStorageMonitorService extends SystemService {
-    static final String TAG = "DeviceStorageMonitorService";
+    private static final String TAG = "DeviceStorageMonitorService";
 
     /**
      * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
@@ -88,68 +77,75 @@
      */
     public static final String EXTRA_SEQUENCE = "seq";
 
-    // TODO: extend to watch and manage caches on all private volumes
+    private static final int MSG_CHECK = 1;
 
-    static final boolean DEBUG = false;
-    static final boolean localLOGV = false;
-
-    static final int DEVICE_MEMORY_WHAT = 1;
-    static final int FORCE_MEMORY_WHAT = 2;
-    private static final int MONITOR_INTERVAL = 1; //in minutes
-
-    private static final int DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES = 12*60; //in minutes
-    private static final long DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD = 2 * 1024 * 1024; // 2MB
-    private static final long DEFAULT_CHECK_INTERVAL = MONITOR_INTERVAL*60*1000;
+    private static final long DEFAULT_LOG_DELTA_BYTES = 64 * TrafficStats.MB_IN_BYTES;
+    private static final long DEFAULT_CHECK_INTERVAL = DateUtils.MINUTE_IN_MILLIS;
 
     // com.android.internal.R.string.low_internal_storage_view_text_no_boot
     // hard codes 250MB in the message as the storage space required for the
     // boot image.
-    private static final long BOOT_IMAGE_STORAGE_REQUIREMENT = 250 * 1024 * 1024;
+    private static final long BOOT_IMAGE_STORAGE_REQUIREMENT = 250 * TrafficStats.MB_IN_BYTES;
 
-    private long mFreeMem;  // on /data
-    private long mFreeMemAfterLastCacheClear;  // on /data
-    private long mLastReportedFreeMem;
-    private long mLastReportedFreeMemTime;
-    boolean mLowMemFlag=false;
-    private boolean mMemFullFlag=false;
-    private final boolean mIsBootImageOnDisk;
-    private final ContentResolver mResolver;
-    private final long mTotalMemory;  // on /data
-    private final StatFs mDataFileStats;
-    private final StatFs mSystemFileStats;
-    private final StatFs mCacheFileStats;
+    private NotificationManager mNotifManager;
 
-    private static final File DATA_PATH = Environment.getDataDirectory();
-    private static final File SYSTEM_PATH = Environment.getRootDirectory();
-    private static final File CACHE_PATH = Environment.getDownloadCacheDirectory();
+    /** Sequence number used for testing */
+    private final AtomicInteger mSeq = new AtomicInteger(1);
+    /** Forced level used for testing */
+    private volatile int mForceLevel = State.LEVEL_UNKNOWN;
 
-    private long mThreadStartTime = -1;
-    boolean mUpdatesStopped;
-    AtomicInteger mSeq = new AtomicInteger(1);
-    boolean mClearSucceeded = false;
-    boolean mClearingCache;
-    private final Intent mStorageLowIntent;
-    private final Intent mStorageOkIntent;
-    private final Intent mStorageFullIntent;
-    private final Intent mStorageNotFullIntent;
-    private CachePackageDataObserver mClearCacheObserver;
+    /** Map from storage volume UUID to internal state */
+    private final ArrayMap<UUID, State> mStates = new ArrayMap<>();
+
+    /**
+     * State for a specific storage volume, including the current "level" that
+     * we've alerted the user and apps about.
+     */
+    private static class State {
+        private static final int LEVEL_UNKNOWN = -1;
+        private static final int LEVEL_NORMAL = 0;
+        private static final int LEVEL_LOW = 1;
+        private static final int LEVEL_FULL = 2;
+
+        /** Last "level" that we alerted about */
+        public int level = LEVEL_NORMAL;
+        /** Last {@link File#getUsableSpace()} that we logged about */
+        public long lastUsableBytes = Long.MAX_VALUE;
+
+        /**
+         * Test if the given level transition is "entering" a specific level.
+         * <p>
+         * As an example, a transition from {@link #LEVEL_NORMAL} to
+         * {@link #LEVEL_FULL} is considered to "enter" both {@link #LEVEL_LOW}
+         * and {@link #LEVEL_FULL}.
+         */
+        private static boolean isEntering(int level, int oldLevel, int newLevel) {
+            return newLevel >= level && (oldLevel < level || oldLevel == LEVEL_UNKNOWN);
+        }
+
+        /**
+         * Test if the given level transition is "leaving" a specific level.
+         * <p>
+         * As an example, a transition from {@link #LEVEL_FULL} to
+         * {@link #LEVEL_NORMAL} is considered to "leave" both
+         * {@link #LEVEL_FULL} and {@link #LEVEL_LOW}.
+         */
+        private static boolean isLeaving(int level, int oldLevel, int newLevel) {
+            return newLevel < level && (oldLevel >= level || oldLevel == LEVEL_UNKNOWN);
+        }
+
+        private static String levelToString(int level) {
+            switch (level) {
+                case State.LEVEL_UNKNOWN: return "UNKNOWN";
+                case State.LEVEL_NORMAL: return "NORMAL";
+                case State.LEVEL_LOW: return "LOW";
+                case State.LEVEL_FULL: return "FULL";
+                default: return Integer.toString(level);
+            }
+        }
+    }
+
     private CacheFileDeletedObserver mCacheFileDeletedObserver;
-    private static final int _TRUE = 1;
-    private static final int _FALSE = 0;
-    // This is the raw threshold that has been set at which we consider
-    // storage to be low.
-    long mMemLowThreshold;
-    // This is the threshold at which we start trying to flush caches
-    // to get below the low threshold limit.  It is less than the low
-    // threshold; we will allow storage to get a bit beyond the limit
-    // before flushing and checking if we are actually low.
-    private long mMemCacheStartTrimThreshold;
-    // This is the threshold that we try to get to when deleting cache
-    // files.  This is greater than the low threshold so that we will flush
-    // more files than absolutely needed, to reduce the frequency that
-    // flushing takes place.
-    private long mMemCacheTrimToThreshold;
-    private long mMemFullThreshold;
 
     /**
      * This string is used for ServiceManager access to this class.
@@ -159,245 +155,107 @@
     private static final String TV_NOTIFICATION_CHANNEL_ID = "devicestoragemonitor.tv";
 
     /**
-    * Handler that checks the amount of disk space on the device and sends a
-    * notification if the device runs low on disk space
-    */
-    private final Handler mHandler = new Handler() {
+     * Handler that checks the amount of disk space on the device and sends a
+     * notification if the device runs low on disk space
+     */
+    private final Handler mHandler = new Handler(IoThread.get().getLooper()) {
         @Override
         public void handleMessage(Message msg) {
-            //don't handle an invalid message
             switch (msg.what) {
-                case DEVICE_MEMORY_WHAT:
-                    checkMemory(msg.arg1 == _TRUE);
-                    return;
-                case FORCE_MEMORY_WHAT:
-                    forceMemory(msg.arg1, msg.arg2);
-                    return;
-                default:
-                    Slog.w(TAG, "Will not process invalid message");
+                case MSG_CHECK:
+                    check();
                     return;
             }
         }
     };
 
-    private class CachePackageDataObserver extends IPackageDataObserver.Stub {
-        public void onRemoveCompleted(String packageName, boolean succeeded) {
-            mClearSucceeded = succeeded;
-            mClearingCache = false;
-            if(localLOGV) Slog.i(TAG, " Clear succeeded:"+mClearSucceeded
-                    +", mClearingCache:"+mClearingCache+" Forcing memory check");
-            postCheckMemoryMsg(false, 0);
+    private State findOrCreateState(UUID uuid) {
+        State state = mStates.get(uuid);
+        if (state == null) {
+            state = new State();
+            mStates.put(uuid, state);
         }
+        return state;
     }
 
-    private void restatDataDir() {
-        try {
-            mDataFileStats.restat(DATA_PATH.getAbsolutePath());
-            mFreeMem = (long) mDataFileStats.getAvailableBlocks() *
-                mDataFileStats.getBlockSize();
-        } catch (IllegalArgumentException e) {
-            // use the old value of mFreeMem
-        }
-        // Allow freemem to be overridden by debug.freemem for testing
-        String debugFreeMem = SystemProperties.get("debug.freemem");
-        if (!"".equals(debugFreeMem)) {
-            mFreeMem = Long.parseLong(debugFreeMem);
-        }
-        // Read the log interval from secure settings
-        long freeMemLogInterval = Settings.Global.getLong(mResolver,
-                Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
-                DEFAULT_FREE_STORAGE_LOG_INTERVAL_IN_MINUTES)*60*1000;
-        //log the amount of free memory in event log
-        long currTime = SystemClock.elapsedRealtime();
-        if((mLastReportedFreeMemTime == 0) ||
-           (currTime-mLastReportedFreeMemTime) >= freeMemLogInterval) {
-            mLastReportedFreeMemTime = currTime;
-            long mFreeSystem = -1, mFreeCache = -1;
-            try {
-                mSystemFileStats.restat(SYSTEM_PATH.getAbsolutePath());
-                mFreeSystem = (long) mSystemFileStats.getAvailableBlocks() *
-                    mSystemFileStats.getBlockSize();
-            } catch (IllegalArgumentException e) {
-                // ignore; report -1
-            }
-            try {
-                mCacheFileStats.restat(CACHE_PATH.getAbsolutePath());
-                mFreeCache = (long) mCacheFileStats.getAvailableBlocks() *
-                    mCacheFileStats.getBlockSize();
-            } catch (IllegalArgumentException e) {
-                // ignore; report -1
-            }
-            EventLog.writeEvent(EventLogTags.FREE_STORAGE_LEFT,
-                                mFreeMem, mFreeSystem, mFreeCache);
-        }
-        // Read the reporting threshold from secure settings
-        long threshold = Settings.Global.getLong(mResolver,
-                Settings.Global.DISK_FREE_CHANGE_REPORTING_THRESHOLD,
-                DEFAULT_DISK_FREE_CHANGE_REPORTING_THRESHOLD);
-        // If mFree changed significantly log the new value
-        long delta = mFreeMem - mLastReportedFreeMem;
-        if (delta > threshold || delta < -threshold) {
-            mLastReportedFreeMem = mFreeMem;
-            EventLog.writeEvent(EventLogTags.FREE_STORAGE_CHANGED, mFreeMem);
-        }
-    }
+    /**
+     * Core logic that checks the storage state of every mounted private volume.
+     * Since this can do heavy I/O, callers should invoke indirectly using
+     * {@link #MSG_CHECK}.
+     */
+    @WorkerThread
+    private void check() {
+        final StorageManager storage = getContext().getSystemService(StorageManager.class);
+        final int seq = mSeq.get();
 
-    private void clearCache() {
-        if (mClearCacheObserver == null) {
-            // Lazy instantiation
-            mClearCacheObserver = new CachePackageDataObserver();
-        }
-        mClearingCache = true;
-        try {
-            if (localLOGV) Slog.i(TAG, "Clearing cache");
-            IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
-                    freeStorageAndNotify(null, mMemCacheTrimToThreshold, mClearCacheObserver);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
-            mClearingCache = false;
-            mClearSucceeded = false;
-        }
-    }
+        // Check every mounted private volume to see if they're low on space
+        for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+            final File file = vol.getPath();
+            final long fullBytes = storage.getStorageFullBytes(file);
+            final long lowBytes = storage.getStorageLowBytes(file);
 
-    void forceMemory(int opts, int seq) {
-        if ((opts&OPTION_UPDATES_STOPPED) == 0) {
-            if (mUpdatesStopped) {
-                mUpdatesStopped = false;
-                checkMemory(true);
-            }
-        } else {
-            mUpdatesStopped = true;
-            final boolean forceLow = (opts&OPTION_STORAGE_LOW) != 0;
-            if (mLowMemFlag != forceLow || (opts&OPTION_FORCE_UPDATE) != 0) {
-                mLowMemFlag = forceLow;
-                if (forceLow) {
-                    sendNotification(seq);
-                } else {
-                    cancelNotification(seq);
+            // Automatically trim cached data when nearing the low threshold;
+            // when it's within 150% of the threshold, we try trimming usage
+            // back to 200% of the threshold.
+            if (file.getUsableSpace() < (lowBytes * 3) / 2) {
+                final PackageManagerService pms = (PackageManagerService) ServiceManager
+                        .getService("package");
+                try {
+                    pms.freeStorage(vol.getFsUuid(), lowBytes * 2, 0);
+                } catch (IOException e) {
+                    Slog.w(TAG, e);
                 }
             }
-        }
-    }
 
-    void checkMemory(boolean checkCache) {
-        if (mUpdatesStopped) {
-            return;
-        }
+            // Send relevant broadcasts and show notifications based on any
+            // recently noticed state transitions.
+            final UUID uuid = StorageManager.convert(vol.getFsUuid());
+            final State state = findOrCreateState(uuid);
+            final long totalBytes = file.getTotalSpace();
+            final long usableBytes = file.getUsableSpace();
 
-        //if the thread that was started to clear cache is still running do nothing till its
-        //finished clearing cache. Ideally this flag could be modified by clearCache
-        // and should be accessed via a lock but even if it does this test will fail now and
-        //hopefully the next time this flag will be set to the correct value.
-        if (mClearingCache) {
-            if(localLOGV) Slog.i(TAG, "Thread already running just skip");
-            //make sure the thread is not hung for too long
-            long diffTime = System.currentTimeMillis() - mThreadStartTime;
-            if(diffTime > (10*60*1000)) {
-                Slog.w(TAG, "Thread that clears cache file seems to run for ever");
-            }
-        } else {
-            restatDataDir();
-            if (localLOGV)  Slog.v(TAG, "freeMemory="+mFreeMem);
-
-            //post intent to NotificationManager to display icon if necessary
-            if (mFreeMem < mMemLowThreshold) {
-                if (checkCache) {
-                    // We are allowed to clear cache files at this point to
-                    // try to get down below the limit, because this is not
-                    // the initial call after a cache clear has been attempted.
-                    // In this case we will try a cache clear if our free
-                    // space has gone below the cache clear limit.
-                    if (mFreeMem < mMemCacheStartTrimThreshold) {
-                        // We only clear the cache if the free storage has changed
-                        // a significant amount since the last time.
-                        if ((mFreeMemAfterLastCacheClear-mFreeMem)
-                                >= ((mMemLowThreshold-mMemCacheStartTrimThreshold)/4)) {
-                            // See if clearing cache helps
-                            // Note that clearing cache is asynchronous and so we do a
-                            // memory check again once the cache has been cleared.
-                            mThreadStartTime = System.currentTimeMillis();
-                            mClearSucceeded = false;
-                            clearCache();
-                        }
-                    }
-                } else {
-                    // This is a call from after clearing the cache.  Note
-                    // the amount of free storage at this point.
-                    mFreeMemAfterLastCacheClear = mFreeMem;
-                    if (!mLowMemFlag) {
-                        // We tried to clear the cache, but that didn't get us
-                        // below the low storage limit.  Tell the user.
-                        Slog.i(TAG, "Running low on memory. Sending notification");
-                        sendNotification(0);
-                        mLowMemFlag = true;
-                    } else {
-                        if (localLOGV) Slog.v(TAG, "Running low on memory " +
-                                "notification already sent. do nothing");
-                    }
-                }
+            int oldLevel = state.level;
+            int newLevel;
+            if (mForceLevel != State.LEVEL_UNKNOWN) {
+                // When in testing mode, use unknown old level to force sending
+                // of any relevant broadcasts.
+                oldLevel = State.LEVEL_UNKNOWN;
+                newLevel = mForceLevel;
+            } else if (usableBytes <= fullBytes) {
+                newLevel = State.LEVEL_FULL;
+            } else if (usableBytes <= lowBytes) {
+                newLevel = State.LEVEL_LOW;
+            } else if (StorageManager.UUID_DEFAULT.equals(uuid) && !isBootImageOnDisk()
+                    && usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) {
+                newLevel = State.LEVEL_LOW;
             } else {
-                mFreeMemAfterLastCacheClear = mFreeMem;
-                if (mLowMemFlag) {
-                    Slog.i(TAG, "Memory available. Cancelling notification");
-                    cancelNotification(0);
-                    mLowMemFlag = false;
-                }
+                newLevel = State.LEVEL_NORMAL;
             }
-            if (!mLowMemFlag && !mIsBootImageOnDisk && mFreeMem < BOOT_IMAGE_STORAGE_REQUIREMENT) {
-                Slog.i(TAG, "No boot image on disk due to lack of space. Sending notification");
-                sendNotification(0);
-                mLowMemFlag = true;
-            }
-            if (mFreeMem < mMemFullThreshold) {
-                if (!mMemFullFlag) {
-                    sendFullNotification();
-                    mMemFullFlag = true;
-                }
-            } else {
-                if (mMemFullFlag) {
-                    cancelFullNotification();
-                    mMemFullFlag = false;
-                }
-            }
-        }
-        if(localLOGV) Slog.i(TAG, "Posting Message again");
-        //keep posting messages to itself periodically
-        postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);
-    }
 
-    void postCheckMemoryMsg(boolean clearCache, long delay) {
-        // Remove queued messages
-        mHandler.removeMessages(DEVICE_MEMORY_WHAT);
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT,
-                clearCache ?_TRUE : _FALSE, 0),
-                delay);
+            // Log whenever we notice drastic storage changes
+            if ((Math.abs(state.lastUsableBytes - usableBytes) > DEFAULT_LOG_DELTA_BYTES)
+                    || oldLevel != newLevel) {
+                EventLogTags.writeStorageState(uuid.toString(), oldLevel, newLevel,
+                        usableBytes, totalBytes);
+                state.lastUsableBytes = usableBytes;
+            }
+
+            updateNotifications(vol, oldLevel, newLevel);
+            updateBroadcasts(vol, oldLevel, newLevel, seq);
+
+            state.level = newLevel;
+        }
+
+        // Loop around to check again in future; we don't remove messages since
+        // there might be an immediate request pending.
+        if (!mHandler.hasMessages(MSG_CHECK)) {
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK),
+                    DEFAULT_CHECK_INTERVAL);
+        }
     }
 
     public DeviceStorageMonitorService(Context context) {
         super(context);
-        mLastReportedFreeMemTime = 0;
-        mResolver = context.getContentResolver();
-        mIsBootImageOnDisk = isBootImageOnDisk();
-        // If these constructors throw IllegalArgumentException, something
-        // is so seriously wrong that we just let the Exception propagate.
-        mDataFileStats = new StatFs(DATA_PATH.getAbsolutePath());
-        mSystemFileStats = new StatFs(SYSTEM_PATH.getAbsolutePath());
-        mCacheFileStats = new StatFs(CACHE_PATH.getAbsolutePath());
-        //initialize total storage on device
-        mTotalMemory = (long)mDataFileStats.getBlockCount() *
-                        mDataFileStats.getBlockSize();
-        mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
-        mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
-                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
-                | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-        mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
-        mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
-                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
-                | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-        mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
-        mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
-        mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
     }
 
     private static boolean isBootImageOnDisk() {
@@ -409,35 +267,20 @@
         return true;
     }
 
-    /**
-    * Initializes the disk space threshold value and posts an empty message to
-    * kickstart the process.
-    */
     @Override
     public void onStart() {
-        // cache storage thresholds
-        Context context = getContext();
-        final StorageManager sm = StorageManager.from(context);
-        mMemLowThreshold = sm.getStorageLowBytes(DATA_PATH);
-        mMemFullThreshold = sm.getStorageFullBytes(DATA_PATH);
-
-        mMemCacheStartTrimThreshold = ((mMemLowThreshold*3)+mMemFullThreshold)/4;
-        mMemCacheTrimToThreshold = mMemLowThreshold
-                + ((mMemLowThreshold-mMemCacheStartTrimThreshold)*2);
-        mFreeMemAfterLastCacheClear = mTotalMemory;
-        checkMemory(true);
+        final Context context = getContext();
+        mNotifManager = context.getSystemService(NotificationManager.class);
 
         mCacheFileDeletedObserver = new CacheFileDeletedObserver();
         mCacheFileDeletedObserver.startWatching();
 
         // Ensure that the notification channel is set up
-        NotificationManager notificationMgr =
-            (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
         PackageManager packageManager = context.getPackageManager();
         boolean isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
 
         if (isTv) {
-            notificationMgr.createNotificationChannel(new NotificationChannel(
+            mNotifManager.createNotificationChannel(new NotificationChannel(
                     TV_NOTIFICATION_CHANNEL_ID,
                     context.getString(
                         com.android.internal.R.string.device_storage_monitor_notification_channel),
@@ -446,23 +289,29 @@
 
         publishBinderService(SERVICE, mRemoteService);
         publishLocalService(DeviceStorageMonitorInternal.class, mLocalService);
+
+        // Kick off pass to examine storage state
+        mHandler.removeMessages(MSG_CHECK);
+        mHandler.obtainMessage(MSG_CHECK).sendToTarget();
     }
 
     private final DeviceStorageMonitorInternal mLocalService = new DeviceStorageMonitorInternal() {
         @Override
         public void checkMemory() {
-            // force an early check
-            postCheckMemoryMsg(true, 0);
+            // Kick off pass to examine storage state
+            mHandler.removeMessages(MSG_CHECK);
+            mHandler.obtainMessage(MSG_CHECK).sendToTarget();
         }
 
         @Override
         public boolean isMemoryLow() {
-            return mLowMemFlag;
+            return Environment.getDataDirectory().getUsableSpace() < getMemoryLowThreshold();
         }
 
         @Override
         public long getMemoryLowThreshold() {
-            return mMemLowThreshold;
+            return getContext().getSystemService(StorageManager.class)
+                    .getStorageLowBytes(Environment.getDataDirectory());
         }
     };
 
@@ -495,8 +344,6 @@
     }
 
     static final int OPTION_FORCE_UPDATE = 1<<0;
-    static final int OPTION_UPDATES_STOPPED = 1<<1;
-    static final int OPTION_STORAGE_LOW = 1<<2;
 
     int parseOptions(Shell shell) {
         String opt;
@@ -519,10 +366,11 @@
                 int opts = parseOptions(shell);
                 getContext().enforceCallingOrSelfPermission(
                         android.Manifest.permission.DEVICE_POWER, null);
+                mForceLevel = State.LEVEL_LOW;
                 int seq = mSeq.incrementAndGet();
-                mHandler.sendMessage(mHandler.obtainMessage(FORCE_MEMORY_WHAT,
-                        opts | OPTION_UPDATES_STOPPED | OPTION_STORAGE_LOW, seq));
                 if ((opts & OPTION_FORCE_UPDATE) != 0) {
+                    mHandler.removeMessages(MSG_CHECK);
+                    mHandler.obtainMessage(MSG_CHECK).sendToTarget();
                     pw.println(seq);
                 }
             } break;
@@ -530,10 +378,11 @@
                 int opts = parseOptions(shell);
                 getContext().enforceCallingOrSelfPermission(
                         android.Manifest.permission.DEVICE_POWER, null);
+                mForceLevel = State.LEVEL_NORMAL;
                 int seq = mSeq.incrementAndGet();
-                mHandler.sendMessage(mHandler.obtainMessage(FORCE_MEMORY_WHAT,
-                        opts | OPTION_UPDATES_STOPPED, seq));
                 if ((opts & OPTION_FORCE_UPDATE) != 0) {
+                    mHandler.removeMessages(MSG_CHECK);
+                    mHandler.obtainMessage(MSG_CHECK).sendToTarget();
                     pw.println(seq);
                 }
             } break;
@@ -541,10 +390,11 @@
                 int opts = parseOptions(shell);
                 getContext().enforceCallingOrSelfPermission(
                         android.Manifest.permission.DEVICE_POWER, null);
+                mForceLevel = State.LEVEL_UNKNOWN;
                 int seq = mSeq.incrementAndGet();
-                mHandler.sendMessage(mHandler.obtainMessage(FORCE_MEMORY_WHAT,
-                        opts, seq));
                 if ((opts & OPTION_FORCE_UPDATE) != 0) {
+                    mHandler.removeMessages(MSG_CHECK);
+                    mHandler.obtainMessage(MSG_CHECK).sendToTarget();
                     pw.println(seq);
                 }
             } break;
@@ -569,145 +419,125 @@
         pw.println("    -f: force a storage change broadcast be sent, prints new sequence.");
     }
 
-    void dumpImpl(FileDescriptor fd, PrintWriter pw, String[] args) {
+    void dumpImpl(FileDescriptor fd, PrintWriter _pw, String[] args) {
+        final IndentingPrintWriter pw = new IndentingPrintWriter(_pw, "  ");
         if (args == null || args.length == 0 || "-a".equals(args[0])) {
-            final Context context = getContext();
-
-            pw.println("Current DeviceStorageMonitor state:");
-
-            pw.print("  mFreeMem=");
-            pw.print(Formatter.formatFileSize(context, mFreeMem));
-            pw.print(" mTotalMemory=");
-            pw.println(Formatter.formatFileSize(context, mTotalMemory));
-
-            pw.print("  mFreeMemAfterLastCacheClear=");
-            pw.println(Formatter.formatFileSize(context, mFreeMemAfterLastCacheClear));
-
-            pw.print("  mLastReportedFreeMem=");
-            pw.print(Formatter.formatFileSize(context, mLastReportedFreeMem));
-            pw.print(" mLastReportedFreeMemTime=");
-            TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw);
+            pw.println("Known volumes:");
+            pw.increaseIndent();
+            for (int i = 0; i < mStates.size(); i++) {
+                final UUID uuid = mStates.keyAt(i);
+                final State state = mStates.valueAt(i);
+                if (StorageManager.UUID_DEFAULT.equals(uuid)) {
+                    pw.println("Default:");
+                } else {
+                    pw.println(uuid + ":");
+                }
+                pw.increaseIndent();
+                pw.printPair("level", State.levelToString(state.level));
+                pw.printPair("lastUsableBytes", state.lastUsableBytes);
+                pw.println();
+                pw.decreaseIndent();
+            }
+            pw.decreaseIndent();
             pw.println();
 
-            if (mUpdatesStopped) {
-                pw.print("  mUpdatesStopped=");
-                pw.print(mUpdatesStopped);
-                pw.print(" mSeq=");
-                pw.println(mSeq.get());
-            } else {
-                pw.print("  mClearSucceeded=");
-                pw.print(mClearSucceeded);
-                pw.print(" mClearingCache=");
-                pw.println(mClearingCache);
-            }
+            pw.printPair("mSeq", mSeq.get());
+            pw.printPair("mForceState", State.levelToString(mForceLevel));
+            pw.println();
+            pw.println();
 
-            pw.print("  mLowMemFlag=");
-            pw.print(mLowMemFlag);
-            pw.print(" mMemFullFlag=");
-            pw.println(mMemFullFlag);
-
-            pw.print("  mMemLowThreshold=");
-            pw.print(Formatter.formatFileSize(context, mMemLowThreshold));
-            pw.print(" mMemFullThreshold=");
-            pw.println(Formatter.formatFileSize(context, mMemFullThreshold));
-
-            pw.print("  mMemCacheStartTrimThreshold=");
-            pw.print(Formatter.formatFileSize(context, mMemCacheStartTrimThreshold));
-            pw.print(" mMemCacheTrimToThreshold=");
-            pw.println(Formatter.formatFileSize(context, mMemCacheTrimToThreshold));
-
-            pw.print("  mIsBootImageOnDisk="); pw.println(mIsBootImageOnDisk);
         } else {
             Shell shell = new Shell();
             shell.exec(mRemoteService, null, fd, null, args, null, new ResultReceiver(null));
         }
     }
 
-    /**
-    * This method sends a notification to NotificationManager to display
-    * an error dialog indicating low disk space and launch the Installer
-    * application
-    */
-    private void sendNotification(int seq) {
+    private void updateNotifications(VolumeInfo vol, int oldLevel, int newLevel) {
         final Context context = getContext();
-        if(localLOGV) Slog.i(TAG, "Sending low memory notification");
-        //log the event to event log with the amount of free storage(in bytes) left on the device
-        EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem);
-        //  Pack up the values and broadcast them to everyone
-        Intent lowMemIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
-        lowMemIntent.putExtra("memory", mFreeMem);
-        lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        NotificationManager notificationMgr =
-                (NotificationManager)context.getSystemService(
-                        Context.NOTIFICATION_SERVICE);
-        CharSequence title = context.getText(
-                com.android.internal.R.string.low_internal_storage_view_title);
-        CharSequence details = context.getText(mIsBootImageOnDisk
-                ? com.android.internal.R.string.low_internal_storage_view_text
-                : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
-        PendingIntent intent = PendingIntent.getActivityAsUser(context, 0,  lowMemIntent, 0,
-                null, UserHandle.CURRENT);
-        Notification notification =
-                new Notification.Builder(context, SystemNotificationChannels.ALERTS)
-                        .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full)
-                        .setTicker(title)
-                        .setColor(context.getColor(
-                            com.android.internal.R.color.system_notification_accent_color))
-                        .setContentTitle(title)
-                        .setContentText(details)
-                        .setContentIntent(intent)
-                        .setStyle(new Notification.BigTextStyle()
-                              .bigText(details))
-                        .setVisibility(Notification.VISIBILITY_PUBLIC)
-                        .setCategory(Notification.CATEGORY_SYSTEM)
-                        .extend(new Notification.TvExtender()
-                                .setChannelId(TV_NOTIFICATION_CHANNEL_ID))
-                        .build();
-        notification.flags |= Notification.FLAG_NO_CLEAR;
-        notificationMgr.notifyAsUser(null, SystemMessage.NOTE_LOW_STORAGE, notification,
-                UserHandle.ALL);
-        Intent broadcast = new Intent(mStorageLowIntent);
-        if (seq != 0) {
-            broadcast.putExtra(EXTRA_SEQUENCE, seq);
+        final UUID uuid = StorageManager.convert(vol.getFsUuid());
+
+        if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {
+            Intent lowMemIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
+            lowMemIntent.putExtra(StorageManager.EXTRA_UUID, uuid);
+            lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            final CharSequence title = context.getText(
+                    com.android.internal.R.string.low_internal_storage_view_title);
+
+            final CharSequence details;
+            if (StorageManager.UUID_DEFAULT.equals(uuid)) {
+                details = context.getText(isBootImageOnDisk()
+                        ? com.android.internal.R.string.low_internal_storage_view_text
+                        : com.android.internal.R.string.low_internal_storage_view_text_no_boot);
+            } else {
+                details = context.getText(
+                        com.android.internal.R.string.low_internal_storage_view_text);
+            }
+
+            PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0,
+                    null, UserHandle.CURRENT);
+            Notification notification =
+                    new Notification.Builder(context, SystemNotificationChannels.ALERTS)
+                            .setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full)
+                            .setTicker(title)
+                            .setColor(context.getColor(
+                                com.android.internal.R.color.system_notification_accent_color))
+                            .setContentTitle(title)
+                            .setContentText(details)
+                            .setContentIntent(intent)
+                            .setStyle(new Notification.BigTextStyle()
+                                  .bigText(details))
+                            .setVisibility(Notification.VISIBILITY_PUBLIC)
+                            .setCategory(Notification.CATEGORY_SYSTEM)
+                            .extend(new Notification.TvExtender()
+                                    .setChannelId(TV_NOTIFICATION_CHANNEL_ID))
+                            .build();
+            notification.flags |= Notification.FLAG_NO_CLEAR;
+            mNotifManager.notifyAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
+                    notification, UserHandle.ALL);
+        } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
+            mNotifManager.cancelAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
+                    UserHandle.ALL);
         }
-        context.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
     }
 
-    /**
-     * Cancels low storage notification and sends OK intent.
-     */
-    private void cancelNotification(int seq) {
-        final Context context = getContext();
-        if(localLOGV) Slog.i(TAG, "Canceling low memory notification");
-        NotificationManager mNotificationMgr =
-                (NotificationManager)context.getSystemService(
-                        Context.NOTIFICATION_SERVICE);
-        //cancel notification since memory has been freed
-        mNotificationMgr.cancelAsUser(null, SystemMessage.NOTE_LOW_STORAGE, UserHandle.ALL);
-
-        context.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
-        Intent broadcast = new Intent(mStorageOkIntent);
-        if (seq != 0) {
-            broadcast.putExtra(EXTRA_SEQUENCE, seq);
+    private void updateBroadcasts(VolumeInfo vol, int oldLevel, int newLevel, int seq) {
+        if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, vol.getFsUuid())) {
+            // We don't currently send broadcasts for secondary volumes
+            return;
         }
-        context.sendBroadcastAsUser(broadcast, UserHandle.ALL);
-    }
 
-    /**
-     * Send a notification when storage is full.
-     */
-    private void sendFullNotification() {
-        if(localLOGV) Slog.i(TAG, "Sending memory full notification");
-        getContext().sendStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
-    }
+        final Intent lowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW)
+                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+                        | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)
+                .putExtra(EXTRA_SEQUENCE, seq);
+        final Intent notLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK)
+                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+                        | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)
+                .putExtra(EXTRA_SEQUENCE, seq);
 
-    /**
-     * Cancels memory full notification and sends "not full" intent.
-     */
-    private void cancelFullNotification() {
-        if(localLOGV) Slog.i(TAG, "Canceling memory full notification");
-        getContext().removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL);
-        getContext().sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL);
+        if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {
+            getContext().sendStickyBroadcastAsUser(lowIntent, UserHandle.ALL);
+        } else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
+            getContext().removeStickyBroadcastAsUser(lowIntent, UserHandle.ALL);
+            getContext().sendBroadcastAsUser(notLowIntent, UserHandle.ALL);
+        }
+
+        final Intent fullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL)
+                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
+                .putExtra(EXTRA_SEQUENCE, seq);
+        final Intent notFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)
+                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
+                .putExtra(EXTRA_SEQUENCE, seq);
+
+        if (State.isEntering(State.LEVEL_FULL, oldLevel, newLevel)) {
+            getContext().sendStickyBroadcastAsUser(fullIntent, UserHandle.ALL);
+        } else if (State.isLeaving(State.LEVEL_FULL, oldLevel, newLevel)) {
+            getContext().removeStickyBroadcastAsUser(fullIntent, UserHandle.ALL);
+            getContext().sendBroadcastAsUser(notFullIntent, UserHandle.ALL);
+        }
     }
 
     private static class CacheFileDeletedObserver extends FileObserver {
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 4044fdb..b937f9d 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -96,6 +96,7 @@
 public class VrManagerService extends SystemService implements EnabledComponentChangeListener{
 
     public static final String TAG = "VrManagerService";
+    static final boolean DBG = false;
 
     private static final int PENDING_STATE_DELAY_MS = 300;
     private static final int EVENT_LOG_SIZE = 32;
@@ -157,7 +158,7 @@
     private void setVrModeAllowedLocked(boolean allowed) {
         if (mVrModeAllowed != allowed) {
             mVrModeAllowed = allowed;
-            Slog.i(TAG, "VR mode is " + ((allowed) ? "allowed" : "disallowed"));
+            if (DBG) Slog.d(TAG, "VR mode is " + ((allowed) ? "allowed" : "disallowed"));
             if (mVrModeAllowed) {
                 consumeAndApplyPendingStateLocked();
             } else {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 221e795..b5476d7 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3492,10 +3492,15 @@
 
             if (win != null) {
                 final int req = win.mAttrs.screenOrientation;
-                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req);
                 if (policy.isKeyguardHostWindow(win.mAttrs)) {
                     mLastKeyguardForcedOrientation = req;
+                    if (mService.mKeyguardGoingAway) {
+                        // Keyguard can't affect the orientation if it is going away...
+                        mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+                        return SCREEN_ORIENTATION_UNSET;
+                    }
                 }
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req);
                 return (mLastWindowForcedOrientation = req);
             }
 
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index fc4ec28..9a9e29a 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -92,17 +92,16 @@
     private final Rect mStableInsets = new Rect();
 
     // The size and position information that describes where the pinned stack will go by default.
+    private int mDefaultMinSize;
     private int mDefaultStackGravity;
     private float mDefaultAspectRatio;
     private Point mScreenEdgeInsets;
+    private int mCurrentMinSize;
 
     // The aspect ratio bounds of the PIP.
     private float mMinAspectRatio;
     private float mMaxAspectRatio;
 
-    // The minimum edge size of the normal PiP bounds.
-    private int mMinSize;
-
     // Temp vars for calculation
     private final DisplayMetrics mTmpMetrics = new DisplayMetrics();
     private final Rect mTmpInsets = new Rect();
@@ -124,6 +123,13 @@
         }
 
         @Override
+        public void setMinEdgeSize(int minEdgeSize) {
+            mHandler.post(() -> {
+                mCurrentMinSize = Math.max(mDefaultMinSize, minEdgeSize);
+            });
+        }
+
+        @Override
         public int getDisplayRotation() {
             synchronized (mService.mWindowMap) {
                 return mDisplayInfo.rotation;
@@ -160,10 +166,12 @@
      */
     private void reloadResources() {
         final Resources res = mService.mContext.getResources();
-        mMinSize = res.getDimensionPixelSize(
+        mDefaultMinSize = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.default_minimal_size_pip_resizable_task);
+        mCurrentMinSize = mDefaultMinSize;
         mDefaultAspectRatio = res.getFloat(
                 com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
+        mAspectRatio = mDefaultAspectRatio;
         final String screenEdgeInsetsDpString = res.getString(
                 com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets);
         final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
@@ -212,11 +220,15 @@
      * Returns the current bounds (or the default bounds if there are no current bounds) with the
      * specified aspect ratio.
      */
-    Rect transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio) {
+    Rect transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio,
+            boolean useCurrentMinEdgeSize) {
         // Save the snap fraction, calculate the aspect ratio based on screen size
         final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
                 getMovementBounds(stackBounds));
-        final Size size = getSize(aspectRatio);
+
+        final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize;
+        final Size size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, minEdgeSize,
+                mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
         final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
         final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
         stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
@@ -228,16 +240,6 @@
     }
 
     /**
-     * @return the size of the PIP based on the given {@param aspectRatio}.
-     */
-    Size getSize(float aspectRatio) {
-        synchronized (mService.mWindowMap) {
-            return mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mMinSize,
-                    mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
-        }
-    }
-
-    /**
      * @return the default bounds to show the PIP when there is no active PIP.
      */
     Rect getDefaultBounds() {
@@ -246,7 +248,8 @@
             getInsetBounds(insetBounds);
 
             final Rect defaultBounds = new Rect();
-            final Size size = getSize(mDefaultAspectRatio);
+            final Size size = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
+                    mDefaultMinSize, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
             Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
                     0, mIsImeShowing ? mImeHeight : 0, defaultBounds);
             return defaultBounds;
@@ -401,7 +404,8 @@
                     getInsetBounds(insetBounds);
                     final Rect normalBounds = getDefaultBounds();
                     if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
-                        transformBoundsToAspectRatio(normalBounds, mAspectRatio);
+                        transformBoundsToAspectRatio(normalBounds, mAspectRatio,
+                                false /* useCurrentMinEdgeSize */);
                     }
                     final Rect animatingBounds = mTmpAnimatingBoundsRect;
                     final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID);
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 0c628ac..989e8f2 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 
 import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
@@ -36,7 +35,8 @@
  */
 public class PinnedStackWindowController extends StackWindowController {
 
-    private Rect mTmpBoundsRect = new Rect();
+    private Rect mTmpFromBounds = new Rect();
+    private Rect mTmpToBounds = new Rect();
 
     public PinnedStackWindowController(int stackId, PinnedStackWindowListener listener,
             int displayId, boolean onTop, Rect outBounds) {
@@ -44,16 +44,16 @@
     }
 
     /**
-     * @param useExistingStackBounds Apply {@param aspectRatio} to the existing target stack bounds
-     *                               if possible
+     * @return the {@param currentStackBounds} transformed to the give {@param aspectRatio}.  If
+     *         {@param currentStackBounds} is null, then the {@param aspectRatio} is applied to the
+     *         default bounds.
      */
-    public Rect getPictureInPictureBounds(float aspectRatio, boolean useExistingStackBounds) {
+    public Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) {
         synchronized (mWindowMap) {
             if (!mService.mSupportsPictureInPicture || mContainer == null) {
                 return null;
             }
 
-            final Rect stackBounds;
             final DisplayContent displayContent = mContainer.getDisplayContent();
             if (displayContent == null) {
                 return null;
@@ -61,18 +61,14 @@
 
             final PinnedStackController pinnedStackController =
                     displayContent.getPinnedStackController();
-            if (useExistingStackBounds) {
-                // If the stack exists, then use its final bounds to calculate the new aspect ratio
-                // bounds
-                stackBounds = new Rect();
-                mContainer.getAnimationOrCurrentBounds(stackBounds);
-            } else {
-                // Otherwise, just calculate the aspect ratio bounds from the default bounds
+            if (stackBounds == null) {
+                // Calculate the aspect ratio bounds from the default bounds
                 stackBounds = pinnedStackController.getDefaultBounds();
             }
 
             if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) {
-                return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio);
+                return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio,
+                        true /* useCurrentMinEdgeSize */);
             } else {
                 return stackBounds;
             }
@@ -104,10 +100,10 @@
                 }
                 schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
 
-                mService.getStackBounds(FULLSCREEN_WORKSPACE_STACK_ID, mTmpBoundsRect);
-                if (!mTmpBoundsRect.isEmpty()) {
+                mService.getStackBounds(FULLSCREEN_WORKSPACE_STACK_ID, mTmpToBounds);
+                if (!mTmpToBounds.isEmpty()) {
                     // If there is a fullscreen bounds, use that
-                    toBounds = new Rect(mTmpBoundsRect);
+                    toBounds = new Rect(mTmpToBounds);
                 } else {
                     // Otherwise, use the display bounds
                     toBounds = new Rect();
@@ -142,16 +138,15 @@
                 return;
             }
 
-            final Rect toBounds = getPictureInPictureBounds(aspectRatio,
-                    true /* useExistingStackBounds */);
-            final Rect targetBounds = new Rect();
-            mContainer.getAnimationOrCurrentBounds(targetBounds);
             final PinnedStackController pinnedStackController =
                     mContainer.getDisplayContent().getPinnedStackController();
 
             if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) != 0) {
-                if (!toBounds.equals(targetBounds)) {
-                    animateResizePinnedStack(toBounds, null /* sourceHintBounds */,
+                mContainer.getAnimationOrCurrentBounds(mTmpFromBounds);
+                mTmpToBounds.set(mTmpFromBounds);
+                getPictureInPictureBounds(aspectRatio, mTmpToBounds);
+                if (!mTmpToBounds.equals(mTmpFromBounds)) {
+                    animateResizePinnedStack(mTmpToBounds, null /* sourceHintBounds */,
                             -1 /* duration */, false /* fromFullscreen */);
                 }
                 pinnedStackController.setAspectRatio(
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 233e75b..fb500bc 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -858,14 +858,20 @@
         final int privateflags = attrs.privateFlags;
         boolean displayHasContent = false;
 
+        if (DEBUG_KEEP_SCREEN_ON && (attrFlags & FLAG_KEEP_SCREEN_ON) != 0
+                && w != mService.mLastWakeLockHoldingWindow) {
+            Slog.d(TAG_KEEP_SCREEN_ON, "handleNotObscuredLocked: " + w
+                    + " has FLAG_KEEP_SCREEN_ON set, hasSurface=" + w.mHasSurface
+                    + ", canBeSeen=" + canBeSeen);
+        }
+
         if (w.mHasSurface && canBeSeen) {
             if ((attrFlags & FLAG_KEEP_SCREEN_ON) != 0) {
                 mHoldScreen = w.mSession;
                 mHoldScreenWindow = w;
             } else if (DEBUG_KEEP_SCREEN_ON && w == mService.mLastWakeLockHoldingWindow) {
                 Slog.d(TAG_KEEP_SCREEN_ON, "handleNotObscuredLocked: " + w + " was holding "
-                        + "screen wakelock but no longer has FLAG_KEEP_SCREEN_ON!!! called by"
-                        + Debug.getCallers(10));
+                        + "screen wakelock but no longer has FLAG_KEEP_SCREEN_ON!!!");
             }
             if (!syswin && w.mAttrs.screenBrightness >= 0 && mScreenBrightness < 0) {
                 mScreenBrightness = w.mAttrs.screenBrightness;
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 8279b51..c080f34 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -77,5 +77,5 @@
     static final boolean DEBUG_UNKNOWN_APP_VISIBILITY = false;
 
     static final String TAG_KEEP_SCREEN_ON = "DebugKeepScreenOn";
-    static final boolean DEBUG_KEEP_SCREEN_ON = false;
+    static final boolean DEBUG_KEEP_SCREEN_ON = true;
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a15891b..3fa0905 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -349,6 +349,7 @@
     private static final int ANIMATION_DURATION_SCALE = 2;
 
     final private KeyguardDisableHandler mKeyguardDisableHandler;
+    boolean mKeyguardGoingAway;
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -2876,6 +2877,12 @@
         }
     }
 
+    public void setKeyguardGoingAway(boolean keyguardGoingAway) {
+        synchronized (mWindowMap) {
+            mKeyguardGoingAway = keyguardGoingAway;
+        }
+    }
+
     // -------------------------------------------------------------
     // Misc IWindowSession methods
     // -------------------------------------------------------------
@@ -6457,7 +6464,7 @@
                     pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
                     pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
                     pw.print(" animator="); pw.println(mAnimatorDurationScaleSetting);
-            pw.print(" mSkipAppTransitionAnimation=");pw.println(mSkipAppTransitionAnimation);
+            pw.print("  mSkipAppTransitionAnimation=");pw.println(mSkipAppTransitionAnimation);
             pw.println("  mLayoutToAnim:");
             mAppTransition.dump(pw, "    ");
         }
@@ -7173,6 +7180,11 @@
         }
 
         @Override
+        public boolean isKeyguardGoingAway() {
+            return WindowManagerService.this.mKeyguardGoingAway;
+        }
+
+        @Override
         public void showGlobalActions() {
             WindowManagerService.this.showGlobalActions();
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index acd7703..44a867c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2202,18 +2202,30 @@
         }
     }
 
-    void prepareWindowToDisplayDuringRelayout(MergedConfiguration mergedConfiguration) {
-        if ((mAttrs.softInputMode & SOFT_INPUT_MASK_ADJUST)
-                == SOFT_INPUT_ADJUST_RESIZE) {
-            mLayoutNeeded = true;
-        }
-        if (isDrawnLw() && mService.okToDisplay()) {
-            mWinAnimator.applyEnterAnimationLocked();
-        }
+    void prepareWindowToDisplayDuringRelayout(MergedConfiguration mergedConfiguration,
+            boolean wasVisible) {
+        // We need to turn on screen regardless of visibility.
         if ((mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "Relayout window turning screen on: " + this);
             mTurnOnScreen = true;
         }
+
+        // If we were already visible, skip rest of preparation.
+        if (wasVisible) {
+            if (DEBUG_VISIBILITY) Slog.v(TAG,
+                    "Already visible and does not turn on screen, skip preparing: " + this);
+            return;
+        }
+
+        if ((mAttrs.softInputMode & SOFT_INPUT_MASK_ADJUST)
+                == SOFT_INPUT_ADJUST_RESIZE) {
+            mLayoutNeeded = true;
+        }
+
+        if (isDrawnLw() && mService.okToDisplay()) {
+            mWinAnimator.applyEnterAnimationLocked();
+        }
+
         if (isConfigChanged()) {
             final Configuration globalConfig = mService.mRoot.getConfiguration();
             final Configuration overrideConfig = getMergedOverrideConfiguration();
@@ -3407,7 +3419,7 @@
             pw.print(prefix); pw.print("mOrientationChanging=");
                     pw.print(mOrientationChanging);
                     pw.print(" mAppFreezing="); pw.print(mAppFreezing);
-                    pw.print(" mTurnOnScreen="); pw.println(mTurnOnScreen);
+                    pw.print(" mTurnOnScreen="); pw.print(mTurnOnScreen);
                     pw.print(" mReportOrientationChanged="); pw.println(mReportOrientationChanged);
         }
         if (mLastFreezeDuration != 0) {
@@ -4348,9 +4360,9 @@
         mLastVisibleLayoutRotation = getDisplayContent().getRotation();
 
         mWinAnimator.mEnteringAnimation = true;
-        if (!wasVisible) {
-            prepareWindowToDisplayDuringRelayout(mergedConfiguration);
-        }
+
+        prepareWindowToDisplayDuringRelayout(mergedConfiguration, wasVisible);
+
         if ((attrChanges & FORMAT_CHANGED) != 0) {
             // If the format can't be changed in place, preserve the old surface until the app draws
             // on the new one. This prevents blinking when we change elevation of freeform and
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d75afcf..73f8d27 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -687,6 +687,7 @@
             mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
                     attrs.getTitle().toString(),
                     width, height, format, flags, this, windowType, ownerUid);
+            mSurfaceFormat = format;
 
             w.setHasSurface(true);
 
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 41da88f..5a72e6b 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -364,11 +364,14 @@
         channel2.enableVibration(false);
         channel2.setGroup(ncg.getId());
         channel2.setLightColor(Color.BLUE);
+        NotificationChannel channel3 = new NotificationChannel("id3", "NAM3", IMPORTANCE_HIGH);
+        channel3.enableVibration(true);
 
         mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
         mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
         mHelper.createNotificationChannel(PKG, UID, channel1, true);
         mHelper.createNotificationChannel(PKG, UID, channel2, false);
+        mHelper.createNotificationChannel(PKG, UID, channel3, false);
         mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true);
 
         mHelper.setShowBadge(PKG, UID, true);
@@ -376,8 +379,9 @@
         mHelper.setImportance(UPDATED_PKG, UID2, IMPORTANCE_NONE);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel1.getId(),
-                channel2.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
-        mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG, UPDATED_PKG}, new int[]{UID, UID2});
+                channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
+        mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG, UPDATED_PKG},
+                new int[]{UID, UID2});
 
         mHelper.setShowBadge(UPDATED_PKG, UID2, true);
 
@@ -388,6 +392,8 @@
         assertEquals(channel1, mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
         compareChannels(channel2,
                 mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
+        compareChannels(channel3,
+                mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
 
         List<NotificationChannelGroup> actualGroups =
                 mHelper.getNotificationChannelGroups(PKG, UID, false).getList();
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 9cfa542..bac1216 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -235,8 +235,7 @@
                     if (mStackId == ActivityManager.StackId.PINNED_STACK_ID) {
                         mStack = new PinnedActivityStack(this, recents, mOnTop) {
                             @Override
-                            Rect getPictureInPictureBounds(float aspectRatio,
-                                    boolean useExistingStackBounds) {
+                            Rect getDefaultPictureInPictureBounds(float aspectRatio) {
                                 return new Rect(50, 50, 100, 100);
                             }
                         };
diff --git a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java b/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
index f287386..54f93a8 100644
--- a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
@@ -32,6 +32,7 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Pair;
 
 import org.mockito.ArgumentMatchers;
@@ -40,6 +41,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 
+@SmallTest
 public class PersistentConnectionTest extends AndroidTestCase {
     private static class MyConnection extends PersistentConnection<IDeviceAdminService> {
         public long uptimeMillis = 12345;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java
index 3819914..175fdd8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyConstantsTest.java
@@ -16,6 +16,7 @@
 package com.android.server.devicepolicy;
 
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
 
 /**
  * Test for {@link DevicePolicyConstants}.
@@ -29,6 +30,7 @@
 
  -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
  */
+@SmallTest
 public class DevicePolicyConstantsTest extends AndroidTestCase {
     private static final String TAG = "DevicePolicyConstantsTest";
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 88fb992..6393b0b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -21,6 +21,8 @@
 import static android.os.UserManagerInternal.CAMERA_DISABLED_LOCALLY;
 import static android.os.UserManagerInternal.CAMERA_NOT_DISABLED;
 
+import static com.android.server.testutis.TestUtils.assertExpectException;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyLong;
@@ -111,6 +113,8 @@
     private static final List<String> OWNER_SETUP_PERMISSIONS = Arrays.asList(
             permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
             permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL);
+    public static final String NOT_DEVICE_OWNER_MSG = "does not own the device";
+    public static final String ONGOING_CALL_MSG = "ongoing call on the device";
 
     // TODO replace all instances of this with explicit {@link #mServiceContext}.
     @Deprecated
@@ -267,19 +271,14 @@
         // 1. Failure cases.
 
         // Caller doesn't have MANAGE_DEVICE_ADMINS.
-        try {
-            dpm.setActiveAdmin(admin1, false);
-            fail("Didn't throw SecurityException");
-        } catch (SecurityException expected) {
-        }
+        assertExpectException(SecurityException.class, /* messageRegex= */ null,
+                () -> dpm.setActiveAdmin(admin1, false));
 
         // Caller has MANAGE_DEVICE_ADMINS, but for different user.
         mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
-        try {
-            dpm.setActiveAdmin(admin1, false, DpmMockContext.CALLER_USER_HANDLE + 1);
-            fail("Didn't throw SecurityException");
-        } catch (SecurityException expected) {
-        }
+
+        assertExpectException(SecurityException.class, /* messageRegex= */ null,
+                () -> dpm.setActiveAdmin(admin1, false, DpmMockContext.CALLER_USER_HANDLE + 1));
     }
 
     /**
@@ -357,11 +356,8 @@
                 anyString());
 
         // 4. Add the same admin1 again without replace, which should throw.
-        try {
-            dpm.setActiveAdmin(admin1, /* replace =*/ false);
-            fail("Didn't throw");
-        } catch (IllegalArgumentException expected) {
-        }
+        assertExpectException(IllegalArgumentException.class, /* messageRegex= */ null,
+                () -> dpm.setActiveAdmin(admin1, /* replace =*/ false));
 
         // 5. Add the same admin1 again with replace, which should succeed.
         dpm.setActiveAdmin(admin1, /* replace =*/ true);
@@ -423,11 +419,8 @@
         assertTrue(dpm.isAdminActive(admin1));
 
         // Add the same admin1 again without replace, which should throw.
-        try {
-            dpm.setActiveAdmin(admin1, /* replace =*/ false);
-            fail("Didn't throw");
-        } catch (IllegalArgumentException expected) {
-        }
+        assertExpectException(IllegalArgumentException.class, /* messageRegex= */ null,
+                () -> dpm.setActiveAdmin(admin1, /* replace =*/ false));
     }
 
     /**
@@ -439,12 +432,9 @@
         // 1. Make sure the caller has proper permissions.
         mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
 
-        try {
-            dpm.setActiveAdmin(adminNoPerm, /* replace =*/ false);
-            fail();
-        } catch (IllegalArgumentException expected) {
-            assertTrue(expected.getMessage().contains(permission.BIND_DEVICE_ADMIN));
-        }
+        assertExpectException(IllegalArgumentException.class,
+                /* messageRegex= */ permission.BIND_DEVICE_ADMIN,
+                () -> dpm.setActiveAdmin(adminNoPerm, /* replace =*/ false));
         assertFalse(dpm.isAdminActive(adminNoPerm));
 
         // Change the target API level to MNC.  Now it can be set as DA.
@@ -473,11 +463,8 @@
         assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
 
         // Directly call the DPMS method with a different userid, which should fail.
-        try {
-            dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE + 1);
-            fail("Didn't throw SecurityException");
-        } catch (SecurityException expected) {
-        }
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                () -> dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE + 1));
 
         // Try to remove active admin with a different caller userid should fail too, without
         // having MANAGE_DEVICE_ADMINS.
@@ -486,11 +473,8 @@
         // Change the caller, and call into DPMS directly with a different user-id.
 
         mContext.binder.callingUid = 1234567;
-        try {
-            dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE);
-            fail("Didn't throw SecurityException");
-        } catch (SecurityException expected) {
-        }
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                () -> dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
     }
 
     /**
@@ -513,13 +497,9 @@
         // 1. User not unlocked.
         when(getServices().userManager.isUserUnlocked(eq(DpmMockContext.CALLER_USER_HANDLE)))
                 .thenReturn(false);
-        try {
-            dpm.removeActiveAdmin(admin1);
-            fail("Didn't throw IllegalStateException");
-        } catch (IllegalStateException expected) {
-            MoreAsserts.assertContainsRegex(
-                    "User must be running and unlocked", expected.getMessage());
-        }
+        assertExpectException(IllegalStateException.class,
+                /* messageRegex= */ "User must be running and unlocked",
+                () -> dpm.removeActiveAdmin(admin1));
 
         assertFalse(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
 
@@ -690,13 +670,9 @@
         // Try to set a profile owner on the same user, which should fail.
         setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID);
         dpm.setActiveAdmin(admin2, /* refreshing= */ true, UserHandle.USER_SYSTEM);
-        try {
-            dpm.setProfileOwner(admin2, "owner-name", UserHandle.USER_SYSTEM);
-            fail("IllegalStateException not thrown");
-        } catch (IllegalStateException expected) {
-            assertTrue("Message was: " + expected.getMessage(),
-                    expected.getMessage().contains("already has a device owner"));
-        }
+        assertExpectException(IllegalStateException.class,
+                /* messageRegex= */ "already has a device owner",
+                () -> dpm.setProfileOwner(admin2, "owner-name", UserHandle.USER_SYSTEM));
 
         // DO admin can't be deactivated.
         dpm.removeActiveAdmin(admin1);
@@ -853,26 +829,14 @@
             assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
         }
 
-        try {
-            dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName());
-            fail();
-        } catch (SecurityException expected) {
-        }
-        try {
-            dpm.getDeviceOwnerComponentOnAnyUser();
-            fail();
-        } catch (SecurityException expected) {
-        }
-        try {
-            dpm.getDeviceOwnerUserId();
-            fail();
-        } catch (SecurityException expected) {
-        }
-        try {
-            dpm.getDeviceOwnerNameOnAnyUser();
-            fail();
-        } catch (SecurityException expected) {
-        }
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                () -> dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                dpm::getDeviceOwnerComponentOnAnyUser);
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                dpm::getDeviceOwnerUserId);
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                dpm::getDeviceOwnerNameOnAnyUser);
 
         mContext.binder.callingUid = DpmMockContext.CALLER_UID;
         // Still no MANAGE_USERS.
@@ -880,26 +844,14 @@
         assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
         assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
 
-        try {
-            dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName());
-            fail();
-        } catch (SecurityException expected) {
-        }
-        try {
-            dpm.getDeviceOwnerComponentOnAnyUser();
-            fail();
-        } catch (SecurityException expected) {
-        }
-        try {
-            dpm.getDeviceOwnerUserId();
-            fail();
-        } catch (SecurityException expected) {
-        }
-        try {
-            dpm.getDeviceOwnerNameOnAnyUser();
-            fail();
-        } catch (SecurityException expected) {
-        }
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                () -> dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                dpm::getDeviceOwnerComponentOnAnyUser);
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                dpm::getDeviceOwnerUserId);
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                dpm::getDeviceOwnerNameOnAnyUser);
 
         // Restore.
         mContext.binder.callingUid = origCallingUser;
@@ -919,13 +871,9 @@
         // Call from a process on the system user.
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
 
-        try {
-            dpm.setDeviceOwner(new ComponentName("a.b.c", ".def"));
-            fail("Didn't throw IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {
-            assertTrue("Message was: " + expected.getMessage(),
-                    expected.getMessage().contains("Invalid component"));
-        }
+        assertExpectException(IllegalArgumentException.class,
+                /* messageRegex= */ "Invalid component",
+                () -> dpm.setDeviceOwner(new ComponentName("a.b.c", ".def")));
     }
 
     public void testSetDeviceOwner_failures() throws Exception {
@@ -972,13 +920,9 @@
 
         // But first pretend the user is locked.  Then it should fail.
         when(getServices().userManager.isUserUnlocked(anyInt())).thenReturn(false);
-        try {
-            dpm.clearDeviceOwnerApp(admin1.getPackageName());
-            fail("Didn't throw IllegalStateException");
-        } catch (IllegalStateException expected) {
-            MoreAsserts.assertContainsRegex(
-                    "User must be running and unlocked", expected.getMessage());
-        }
+        assertExpectException(IllegalStateException.class,
+                /* messageRegex= */ "User must be running and unlocked",
+                () -> dpm.clearDeviceOwnerApp(admin1.getPackageName()));
 
         when(getServices().userManager.isUserUnlocked(anyInt())).thenReturn(true);
         reset(getServices().userManagerInternal);
@@ -1037,12 +981,9 @@
         doReturn(DpmMockContext.CALLER_UID).when(getServices().packageManager).getPackageUidAsUser(
                 eq(admin1.getPackageName()),
                 anyInt());
-        try {
-            dpm.clearDeviceOwnerApp(admin1.getPackageName());
-            fail("Didn't throw");
-        } catch (SecurityException e) {
-            assertEquals("clearDeviceOwner can only be called by the device owner", e.getMessage());
-        }
+        assertExpectException(SecurityException.class,
+                /* messageRegex =*/ "clearDeviceOwner can only be called by the device owner",
+                () -> dpm.clearDeviceOwnerApp(admin1.getPackageName()));
 
         // DO shouldn't be removed.
         assertTrue(dpm.isDeviceManaged());
@@ -1060,13 +1001,10 @@
         mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
         runAsCaller(mServiceContext, dpms, dpm -> {
             dpm.setActiveAdmin(admin2, /* refreshing= */ true, DpmMockContext.CALLER_USER_HANDLE);
-            try {
-                dpm.setDeviceOwner(admin2, "owner-name", DpmMockContext.CALLER_USER_HANDLE);
-                fail("IllegalStateException not thrown");
-            } catch (IllegalStateException expected) {
-                assertTrue("Message was: " + expected.getMessage(),
-                        expected.getMessage().contains("already has a profile owner"));
-            }
+            assertExpectException(IllegalStateException.class,
+                    /* messageRegex= */ "already has a profile owner",
+                    () -> dpm.setDeviceOwner(admin2, "owner-name",
+                            DpmMockContext.CALLER_USER_HANDLE));
         });
     }
 
@@ -1081,16 +1019,12 @@
         // First try when the user is locked, which should fail.
         when(getServices().userManager.isUserUnlocked(anyInt()))
                 .thenReturn(false);
-        try {
-            dpm.clearProfileOwner(admin1);
-            fail("Didn't throw IllegalStateException");
-        } catch (IllegalStateException expected) {
-            MoreAsserts.assertContainsRegex(
-                    "User must be running and unlocked", expected.getMessage());
-        }
+        assertExpectException(IllegalStateException.class,
+                /* messageRegex= */ "User must be running and unlocked",
+                () -> dpm.clearProfileOwner(admin1));
+
         // Clear, really.
-        when(getServices().userManager.isUserUnlocked(anyInt()))
-                .thenReturn(true);
+        when(getServices().userManager.isUserUnlocked(anyInt())).thenReturn(true);
         dpm.clearProfileOwner(admin1);
 
         // Check
@@ -1357,12 +1291,8 @@
         mContext.binder.callingUid = RESTRICTIONS_DELEGATE_UID;
         mContext.packageName = RESTRICTIONS_DELEGATE;
 
-        // DPMS throws a SecurityException
-        try {
-            dpm.installCaCert(null, null);
-            fail("Didn't throw SecurityException on unauthorized access");
-        } catch (SecurityException expected) {
-        }
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                () -> dpm.installCaCert(null, null));
 
         // On calling install certificate APIs from an authorized process
         mContext.binder.callingUid = CERT_DELEGATE_UID;
@@ -1384,11 +1314,8 @@
         // DPMS does not allow access to ex-delegate
         mContext.binder.callingUid = CERT_DELEGATE_UID;
         mContext.packageName = CERT_DELEGATE;
-        try {
-            dpm.installCaCert(null, null);
-            fail("Didn't throw SecurityException on unauthorized access");
-        } catch (SecurityException expected) {
-        }
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                () -> dpm.installCaCert(null, null));
 
         // But still allows access to other existing delegates
         mContext.binder.callingUid = RESTRICTIONS_DELEGATE_UID;
@@ -1405,6 +1332,8 @@
 
         final String nonExistAppRestrictionsManagerPackage = "com.google.app.restrictions.manager2";
         final String appRestrictionsManagerPackage = "com.google.app.restrictions.manager";
+        final String nonDelegateExceptionMessageRegex =
+                "Caller with uid \\d+ is not a delegate of scope delegation-app-restrictions.";
         final int appRestrictionsManagerAppId = 20987;
         final int appRestrictionsManagerUid = setupPackageInPackageManager(
                 appRestrictionsManagerPackage, appRestrictionsManagerAppId);
@@ -1414,24 +1343,10 @@
         mContext.binder.callingUid = DpmMockContext.CALLER_UID;
         mContext.packageName = admin1.getPackageName();
         assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
-        Bundle rest = new Bundle();
+        final Bundle rest = new Bundle();
         rest.putString("KEY_STRING", "Foo1");
-        try {
-            dpm.setApplicationRestrictions(null, "pkg1", rest);
-            fail("Didn't throw expected SecurityException");
-        } catch (SecurityException expected) {
-            MoreAsserts.assertContainsRegex(
-                    "Caller with uid \\d+ is not a delegate of scope delegation-app-restrictions.",
-                    expected.getMessage());
-        }
-        try {
-            dpm.getApplicationRestrictions(null, "pkg1");
-            fail("Didn't throw expected SecurityException");
-        } catch (SecurityException expected) {
-            MoreAsserts.assertContainsRegex(
-                    "Caller with uid \\d+ is not a delegate of scope delegation-app-restrictions.",
-                    expected.getMessage());
-        }
+        assertExpectException(SecurityException.class, nonDelegateExceptionMessageRegex,
+                () -> dpm.setApplicationRestrictions(null, "pkg1", rest));
 
         // Check via the profile owner that no restrictions were set.
         mContext.binder.callingUid = DpmMockContext.CALLER_UID;
@@ -1439,14 +1354,10 @@
         assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg1").size());
 
         // Check the API does not allow setting a non-existent package
-        try {
-            dpm.setApplicationRestrictionsManagingPackage(admin1,
-                    nonExistAppRestrictionsManagerPackage);
-            fail("Non-existent app set as app restriction manager.");
-        } catch (PackageManager.NameNotFoundException expected) {
-            MoreAsserts.assertContainsRegex(
-                    nonExistAppRestrictionsManagerPackage, expected.getMessage());
-        }
+        assertExpectException(PackageManager.NameNotFoundException.class,
+                /* messageRegex= */ nonExistAppRestrictionsManagerPackage,
+                () -> dpm.setApplicationRestrictionsManagingPackage(
+                        admin1, nonExistAppRestrictionsManagerPackage));
 
         // Let appRestrictionsManagerPackage manage app restrictions
         dpm.setApplicationRestrictionsManagingPackage(admin1, appRestrictionsManagerPackage);
@@ -1466,14 +1377,8 @@
         mContext.binder.callingUid = UserHandle.getUid(
                 UserHandle.USER_SYSTEM, appRestrictionsManagerAppId);
         assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
-        try {
-            dpm.setApplicationRestrictions(null, "pkg1", rest);
-            fail("Didn't throw expected SecurityException");
-        } catch (SecurityException expected) {
-            MoreAsserts.assertContainsRegex(
-                    "Caller with uid \\d+ is not a delegate of scope delegation-app-restrictions.",
-                    expected.getMessage());
-        }
+        assertExpectException(SecurityException.class, nonDelegateExceptionMessageRegex,
+                () -> dpm.setApplicationRestrictions(null, "pkg1", rest));
 
         // The DPM is still able to manage app restrictions, even if it allowed another app to do it
         // too.
@@ -1489,14 +1394,8 @@
         mContext.binder.callingUid = appRestrictionsManagerUid;
         mContext.packageName = appRestrictionsManagerPackage;
         assertFalse(dpm.isCallerApplicationRestrictionsManagingPackage());
-        try {
-            dpm.setApplicationRestrictions(null, "pkg1", null);
-            fail("Didn't throw expected SecurityException");
-        } catch (SecurityException expected) {
-            MoreAsserts.assertContainsRegex(
-                    "Caller with uid \\d+ is not a delegate of scope delegation-app-restrictions.",
-                    expected.getMessage());
-        }
+        assertExpectException(SecurityException.class, nonDelegateExceptionMessageRegex,
+                () -> dpm.setApplicationRestrictions(null, "pkg1", null));
     }
 
     public void testSetUserRestriction_asDo() throws Exception {
@@ -1519,7 +1418,7 @@
                 UserHandle.USER_SYSTEM));
 
         // Check that the user restrictions that are enabled by default are set. Then unset them.
-        String[] defaultRestrictions = UserRestrictionsUtils
+        final String[] defaultRestrictions = UserRestrictionsUtils
                 .getDefaultEnabledForDeviceOwner().toArray(new String[0]);
         DpmTestUtils.assertRestrictions(
                 DpmTestUtils.newRestrictions(defaultRestrictions),
@@ -1875,33 +1774,21 @@
         setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
 
         // Test 1. Caller doesn't have DO or DA.
-        try {
-            dpm.getWifiMacAddress(admin1);
-            fail();
-        } catch (SecurityException e) {
-            MoreAsserts.assertContainsRegex("No active admin", e.getMessage());
-        }
+        assertExpectException(SecurityException.class, /* messageRegex= */ "No active admin",
+                () -> dpm.getWifiMacAddress(admin1));
 
         // DO needs to be an DA.
         dpm.setActiveAdmin(admin1, /* replace =*/ false);
         assertTrue(dpm.isAdminActive(admin1));
 
         // Test 2. Caller has DA, but not DO.
-        try {
-            dpm.getWifiMacAddress(admin1);
-            fail();
-        } catch (SecurityException e) {
-            MoreAsserts.assertContainsRegex("does not own the device", e.getMessage());
-        }
+        assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG,
+                () -> dpm.getWifiMacAddress(admin1));
 
         // Test 3. Caller has PO, but not DO.
         assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM));
-        try {
-            dpm.getWifiMacAddress(admin1);
-            fail();
-        } catch (SecurityException e) {
-            MoreAsserts.assertContainsRegex("does not own the device", e.getMessage());
-        }
+        assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG,
+                () -> dpm.getWifiMacAddress(admin1));
 
         // Remove PO.
         dpm.clearProfileOwner(admin1);
@@ -1936,21 +1823,13 @@
         // Set admin1 as DA.
         dpm.setActiveAdmin(admin1, false);
         assertTrue(dpm.isAdminActive(admin1));
-        try {
-            dpm.reboot(admin1);
-            fail("DA calls DPM.reboot(), did not throw expected SecurityException");
-        } catch (SecurityException expected) {
-            MoreAsserts.assertContainsRegex("does not own the device", expected.getMessage());
-        }
+        assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG,
+                () -> dpm.reboot(admin1));
 
         // Set admin1 as PO.
         assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM));
-        try {
-            dpm.reboot(admin1);
-            fail("PO calls DPM.reboot(), did not throw expected SecurityException");
-        } catch (SecurityException expected) {
-            MoreAsserts.assertContainsRegex("does not own the device", expected.getMessage());
-        }
+        assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG,
+                () -> dpm.reboot(admin1));
 
         // Remove PO and add DO.
         dpm.clearProfileOwner(admin1);
@@ -1961,22 +1840,14 @@
         // Set current call state of device to ringing.
         when(getServices().telephonyManager.getCallState())
                 .thenReturn(TelephonyManager.CALL_STATE_RINGING);
-        try {
-            dpm.reboot(admin1);
-            fail("DPM.reboot() called when receiveing a call, should thrown IllegalStateException");
-        } catch (IllegalStateException expected) {
-            MoreAsserts.assertContainsRegex("ongoing call on the device", expected.getMessage());
-        }
+        assertExpectException(IllegalStateException.class, /* messageRegex= */ ONGOING_CALL_MSG,
+                () -> dpm.reboot(admin1));
 
         // Set current call state of device to dialing/active.
         when(getServices().telephonyManager.getCallState())
                 .thenReturn(TelephonyManager.CALL_STATE_OFFHOOK);
-        try {
-            dpm.reboot(admin1);
-            fail("DPM.reboot() called when dialing, should thrown IllegalStateException");
-        } catch (IllegalStateException expected) {
-            MoreAsserts.assertContainsRegex("ongoing call on the device", expected.getMessage());
-        }
+        assertExpectException(IllegalStateException.class, /* messageRegex= */ ONGOING_CALL_MSG,
+                () -> dpm.reboot(admin1));
 
         // Set current call state of device to idle.
         when(getServices().telephonyManager.getCallState()).thenReturn(TelephonyManager.CALL_STATE_IDLE);
@@ -2003,31 +1874,20 @@
 
         // Only system can call the per user versions.
         {
-            try {
-                dpm.getShortSupportMessageForUser(admin1,
-                        DpmMockContext.CALLER_USER_HANDLE);
-                fail("Only system should be able to call getXXXForUser versions");
-            } catch (SecurityException expected) {
-                MoreAsserts.assertContainsRegex("message for user", expected.getMessage());
-            }
-            try {
-                dpm.getLongSupportMessageForUser(admin1,
-                        DpmMockContext.CALLER_USER_HANDLE);
-                fail("Only system should be able to call getXXXForUser versions");
-            } catch (SecurityException expected) {
-                MoreAsserts.assertContainsRegex("message for user", expected.getMessage());
-            }
+            assertExpectException(SecurityException.class, /* messageRegex= */ "message for user",
+                    () -> dpm.getShortSupportMessageForUser(admin1,
+                            DpmMockContext.CALLER_USER_HANDLE));
+            assertExpectException(SecurityException.class, /* messageRegex= */ "message for user",
+                    () -> dpm.getLongSupportMessageForUser(admin1,
+                        DpmMockContext.CALLER_USER_HANDLE));
         }
 
         // Can't set message for admin in another uid.
         {
             mContext.binder.callingUid = DpmMockContext.CALLER_UID + 1;
-            try {
-                dpm.setShortSupportMessage(admin1, "Some text");
-                fail("Admins should only be able to change their own support text.");
-            } catch (SecurityException expected) {
-                MoreAsserts.assertContainsRegex("is not owned by uid", expected.getMessage());
-            }
+            assertExpectException(SecurityException.class,
+                    /* messageRegex= */ "is not owned by uid",
+                    () -> dpm.setShortSupportMessage(admin1, "Some text"));
             mContext.binder.callingUid = DpmMockContext.CALLER_UID;
         }
 
@@ -2105,8 +1965,7 @@
         assertEquals(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS, intent.getAction());
         assertEquals(UserHandle.getUserId(DpmMockContext.CALLER_SYSTEM_USER_UID),
                 intent.getIntExtra(Intent.EXTRA_USER_ID, -1));
-        assertEquals(admin1,
-                (ComponentName) intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN));
+        assertEquals(admin1, intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN));
         assertEquals(UserManager.DISALLOW_ADJUST_VOLUME,
                 intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
 
@@ -2242,24 +2101,17 @@
 
     public void testSetUserProvisioningState_unprivileged() throws Exception {
         setupProfileOwner();
-        try {
-            dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
-                    DpmMockContext.CALLER_USER_HANDLE);
-            fail("Expected SecurityException");
-        } catch (SecurityException expected) {
-        }
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                () -> dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
+                        DpmMockContext.CALLER_USER_HANDLE));
     }
 
     public void testSetUserProvisioningState_noManagement() {
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
-        try {
-            dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
-                    DpmMockContext.CALLER_USER_HANDLE);
-            fail("IllegalStateException expected");
-        } catch (IllegalStateException e) {
-            MoreAsserts.assertContainsRegex("change provisioning state unless a .* owner is set",
-                    e.getMessage());
-        }
+        assertExpectException(IllegalStateException.class,
+                /* messageRegex= */ "change provisioning state unless a .* owner is set",
+                () -> dpm.setUserProvisioningState(DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
+                        DpmMockContext.CALLER_USER_HANDLE));
         assertEquals(DevicePolicyManager.STATE_USER_UNMANAGED, dpm.getUserProvisioningState());
     }
 
@@ -2325,15 +2177,11 @@
         setupProfileOwner();
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
-        try {
-            exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
-                    DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
-                    DevicePolicyManager.STATE_USER_UNMANAGED);
-            fail("Expected IllegalStateException");
-        } catch (IllegalStateException e) {
-            MoreAsserts.assertContainsRegex("Cannot move to user provisioning state",
-                    e.getMessage());
-        }
+        assertExpectException(IllegalStateException.class,
+                /* messageRegex= */ "Cannot move to user provisioning state",
+                () -> exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+                        DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
+                        DevicePolicyManager.STATE_USER_UNMANAGED));
     }
 
     public void testSetUserProvisioningState_illegalTransitionToAnotherInProgressState()
@@ -2341,15 +2189,11 @@
         setupProfileOwner();
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
 
-        try {
-            exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
-                    DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE,
-                    DevicePolicyManager.STATE_USER_SETUP_COMPLETE);
-            fail("Expected IllegalStateException");
-        } catch (IllegalStateException e) {
-            MoreAsserts.assertContainsRegex("Cannot move to user provisioning state",
-                    e.getMessage());
-        }
+        assertExpectException(IllegalStateException.class,
+                /* messageRegex= */ "Cannot move to user provisioning state",
+                () -> exerciseUserProvisioningTransitions(DpmMockContext.CALLER_USER_HANDLE,
+                        DevicePolicyManager.STATE_USER_SETUP_INCOMPLETE,
+                        DevicePolicyManager.STATE_USER_SETUP_COMPLETE));
     }
 
     private void exerciseUserProvisioningTransitions(int userId, int... states) {
@@ -2512,11 +2356,8 @@
                 DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS);
 
         // negative value
-        try {
-            dpm.setRequiredStrongAuthTimeout(admin1, -ONE_MINUTE);
-            fail("Didn't throw IllegalArgumentException");
-        } catch (IllegalArgumentException iae) {
-        }
+        assertExpectException(IllegalArgumentException.class, /* messageRegex= */ null,
+                () -> dpm.setRequiredStrongAuthTimeout(admin1, -ONE_MINUTE));
     }
 
     private void verifyScreenTimeoutCall(Integer expectedTimeout,
@@ -3097,32 +2938,23 @@
 
     public void testCheckProvisioningPreCondition_permission() {
         // GIVEN the permission MANAGE_PROFILE_AND_DEVICE_OWNERS is not granted
-        try {
-            dpm.checkProvisioningPreCondition(
-                    DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, "some.package");
-            fail("Didn't throw SecurityException");
-        } catch (SecurityException expected) {
-        }
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                () -> dpm.checkProvisioningPreCondition(
+                        DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, "some.package"));
     }
 
     public void testForceUpdateUserSetupComplete_permission() {
         // GIVEN the permission MANAGE_PROFILE_AND_DEVICE_OWNERS is not granted
-        try {
-            dpm.forceUpdateUserSetupComplete();
-            fail("Didn't throw SecurityException");
-        } catch (SecurityException expected) {
-        }
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                () -> dpm.forceUpdateUserSetupComplete());
     }
 
     public void testForceUpdateUserSetupComplete_systemUser() {
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
         // GIVEN calling from user 20
         mContext.binder.callingUid = DpmMockContext.CALLER_UID;
-        try {
-            dpm.forceUpdateUserSetupComplete();
-            fail("Didn't throw SecurityException");
-        } catch (SecurityException expected) {
-        }
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                () -> dpm.forceUpdateUserSetupComplete());
     }
 
     public void testForceUpdateUserSetupComplete_userbuild() {
@@ -3492,16 +3324,10 @@
         // Managed profile is unaffiliated - shouldn't be able to setLockTaskPackages.
         mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
         final String[] poPackages = {"poPackage1", "poPackage2"};
-        try {
-            dpm.setLockTaskPackages(adminDifferentPackage, poPackages);
-            fail("Didn't throw expected security exception.");
-        } catch (SecurityException expected) {
-        }
-        try {
-            dpm.getLockTaskPackages(adminDifferentPackage);
-            fail("Didn't throw expected security exception.");
-        } catch (SecurityException expected) {
-        }
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                () -> dpm.setLockTaskPackages(adminDifferentPackage, poPackages));
+        assertExpectException(SecurityException.class, /* messageRegex =*/ null,
+                () -> dpm.getLockTaskPackages(adminDifferentPackage));
         assertFalse(dpm.isLockTaskPermitted("doPackage1"));
 
         // Setting same affiliation ids
@@ -3610,13 +3436,10 @@
                 UserHandle.of(MANAGED_PROFILE_USER_ID)))
                 .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
         mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
-        try {
-            // The PO is not allowed to remove the profile if the user restriction was set on the
-            // profile by the system
-            dpm.wipeData(0);
-            fail("SecurityException not thrown");
-        } catch (SecurityException expected) {
-        }
+        // The PO is not allowed to remove the profile if the user restriction was set on the
+        // profile by the system
+        assertExpectException(SecurityException.class, /* messageRegex= */ null,
+                () -> dpm.wipeData(0));
     }
 
     public void testWipeDataDeviceOwner() throws Exception {
@@ -3637,13 +3460,10 @@
                 UserManager.DISALLOW_FACTORY_RESET,
                 UserHandle.SYSTEM))
                 .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
-        try {
-            // The DO is not allowed to wipe the device if the user restriction was set
-            // by the system
-            dpm.wipeData(0);
-            fail("SecurityException not thrown");
-        } catch (SecurityException expected) {
-        }
+        // The DO is not allowed to wipe the device if the user restriction was set
+        // by the system
+        assertExpectException(SecurityException.class, /* messageRegex= */ null,
+                () -> dpm.wipeData(0));
     }
 
     public void testMaximumFailedPasswordAttemptsReachedManagedProfile() throws Exception {
@@ -3783,11 +3603,8 @@
         // A regular app cannot retrieve permission grant state.
         mContext.binder.callingUid = setupPackageInPackageManager(app1, 1);
         mContext.packageName = app1;
-        try {
-            dpm.getPermissionGrantState(null, app1, permission);
-            fail("Didn't throw SecurityException");
-        } catch (SecurityException expected) {
-        }
+        assertExpectException(SecurityException.class, /* messageRegex= */ null,
+                () -> dpm.getPermissionGrantState(null, app1, permission));
 
         // Profile owner can retrieve permission grant state.
         mContext.binder.callingUid = DpmMockContext.CALLER_UID;
@@ -3803,11 +3620,9 @@
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
         // test token validation
-        try {
-            dpm.setResetPasswordToken(admin1, new byte[31]);
-            fail("should not have accepted tokens too short");
-        } catch (IllegalArgumentException expected) {
-        }
+        assertExpectException(IllegalArgumentException.class, /* messageRegex= */ null,
+                () -> dpm.setResetPasswordToken(admin1, new byte[31]));
+
         // test adding a token
         final byte[] token = new byte[32];
         final long handle = 123456;
@@ -4125,12 +3940,10 @@
         assertTrue(dpms.setPermittedCrossProfileNotificationListeners(
                 admin1, Collections.singletonList(permittedListener)));
 
-        try {
-            dpms.isNotificationListenerServicePermitted(
-                permittedListener, MANAGED_PROFILE_USER_ID);
-            fail("isNotificationListenerServicePermitted should throw if not called from System");
-        } catch (SecurityException expected) {
-        }
+        // isNotificationListenerServicePermitted should throw if not called from System.
+        assertExpectException(SecurityException.class, /* messageRegex= */ null,
+                () -> dpms.isNotificationListenerServicePermitted(
+                        permittedListener, MANAGED_PROFILE_USER_ID));
 
         mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
         assertTrue(dpms.isNotificationListenerServicePermitted(
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index 909a835..85835f7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -20,6 +20,7 @@
 
 import android.content.ComponentName;
 import android.os.UserHandle;
+import android.test.suitebuilder.annotation.SmallTest;
 
 /**
  * Tests for the DeviceOwner object that saves & loads device and policy owner information.
@@ -32,6 +33,7 @@
 
  (mmma frameworks/base/services/tests/servicestests/ for non-ninja build)
  */
+@SmallTest
 public class OwnersTest extends DpmTestBase {
     public void testUpgrade01() throws Exception {
         getServices().addUsers(10, 11, 20, 21);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 7de46d9..84cca0e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -96,7 +96,7 @@
             storageDir.mkdirs();
         }
 
-        mSpManager = new MockSyntheticPasswordManager(mStorage, mGateKeeperService);
+        mSpManager = new MockSyntheticPasswordManager(mStorage, mGateKeeperService, mUserManager);
         mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils,
                 mStorage, mGateKeeperService, mKeyStore, mStorageManager, mActivityManager,
                 mSpManager);
@@ -164,4 +164,3 @@
         assertFalse(Arrays.equals(expected, actual));
     }
 }
-
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 2f0ac38..cb32492 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -16,6 +16,11 @@
 
 package com.android.server.locksettings;
 
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
@@ -44,21 +49,23 @@
     }
 
     public void testCreatePasswordPrimaryUser() throws RemoteException {
-        testCreateCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD);
+        testCreateCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD,
+                PASSWORD_QUALITY_ALPHABETIC);
     }
 
     public void testCreatePatternPrimaryUser() throws RemoteException {
-        testCreateCredential(PRIMARY_USER_ID, "123456789", CREDENTIAL_TYPE_PATTERN);
+        testCreateCredential(PRIMARY_USER_ID, "123456789", CREDENTIAL_TYPE_PATTERN,
+                PASSWORD_QUALITY_SOMETHING);
     }
 
     public void testChangePasswordPrimaryUser() throws RemoteException {
         testChangeCredentials(PRIMARY_USER_ID, "78963214", CREDENTIAL_TYPE_PATTERN,
-                "asdfghjk", CREDENTIAL_TYPE_PASSWORD);
+                "asdfghjk", CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC);
     }
 
     public void testChangePatternPrimaryUser() throws RemoteException {
         testChangeCredentials(PRIMARY_USER_ID, "!£$%^&*(())", CREDENTIAL_TYPE_PASSWORD,
-                "1596321", CREDENTIAL_TYPE_PATTERN);
+                "1596321", CREDENTIAL_TYPE_PATTERN, PASSWORD_QUALITY_SOMETHING);
     }
 
     public void testChangePasswordFailPrimaryUser() throws RemoteException {
@@ -68,13 +75,14 @@
 
         try {
             mService.setLockCredential("newpwd", CREDENTIAL_TYPE_PASSWORD, "badpwd",
-                    PRIMARY_USER_ID);
+                    PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
             fail("Did not fail when enrolling using incorrect credential");
         } catch (RemoteException expected) {
             assertTrue(expected.getMessage().equals(FAILED_MESSAGE));
         }
         try {
-            mService.setLockCredential("newpwd", CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
+            mService.setLockCredential("newpwd", CREDENTIAL_TYPE_PASSWORD, null,
+                    PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
             fail("Did not fail when enrolling using incorrect credential");
         } catch (RemoteException expected) {
             assertTrue(expected.getMessage().equals(FAILED_MESSAGE));
@@ -85,7 +93,8 @@
     public void testClearPasswordPrimaryUser() throws RemoteException {
         final String PASSWORD = "password";
         initializeStorageWithCredential(PRIMARY_USER_ID, PASSWORD, CREDENTIAL_TYPE_PASSWORD, 1234);
-        mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, PASSWORD, PRIMARY_USER_ID);
+        mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, PASSWORD,
+                PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
         assertFalse(mService.havePassword(PRIMARY_USER_ID));
         assertFalse(mService.havePattern(PRIMARY_USER_ID));
         assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
@@ -94,7 +103,7 @@
     public void testManagedProfileUnifiedChallenge() throws RemoteException {
         final String UnifiedPassword = "testManagedProfileUnifiedChallenge-pwd";
         mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
-                PRIMARY_USER_ID);
+                PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID);
         mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
         final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
         final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
@@ -129,14 +138,14 @@
         mStorageManager.setIgnoreBadUnlock(true);
         // Change primary password and verify that profile SID remains
         mService.setLockCredential("pwd", LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
-                UnifiedPassword, PRIMARY_USER_ID);
+                UnifiedPassword, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
         mStorageManager.setIgnoreBadUnlock(false);
         assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
         assertNull(mGateKeeperService.getAuthToken(TURNED_OFF_PROFILE_USER_ID));
 
         // Clear unified challenge
         mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, UnifiedPassword,
-                PRIMARY_USER_ID);
+                PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
         assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
         assertEquals(0, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
         assertEquals(0, mGateKeeperService.getSecureUserId(TURNED_OFF_PROFILE_USER_ID));
@@ -146,14 +155,14 @@
         final String primaryPassword = "testManagedProfileSeparateChallenge-primary";
         final String profilePassword = "testManagedProfileSeparateChallenge-profile";
         mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
-                PRIMARY_USER_ID);
+                PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID);
         /* Currently in LockSettingsService.setLockCredential, unlockUser() is called with the new
          * credential as part of verifyCredential() before the new credential is committed in
          * StorageManager. So we relax the check in our mock StorageManager to allow that.
          */
         mStorageManager.setIgnoreBadUnlock(true);
         mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
-                MANAGED_PROFILE_USER_ID);
+                PASSWORD_QUALITY_COMPLEX, MANAGED_PROFILE_USER_ID);
         mStorageManager.setIgnoreBadUnlock(false);
 
         final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
@@ -180,7 +189,7 @@
         // Change primary credential and make sure we don't affect profile
         mStorageManager.setIgnoreBadUnlock(true);
         mService.setLockCredential("pwd", LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
-                primaryPassword, PRIMARY_USER_ID);
+                primaryPassword, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
         mStorageManager.setIgnoreBadUnlock(false);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
                 profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
@@ -188,17 +197,17 @@
         assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
     }
 
-    private void testCreateCredential(int userId, String credential, int type)
+    private void testCreateCredential(int userId, String credential, int type, int quality)
             throws RemoteException {
-        mService.setLockCredential(credential, type, null, userId);
+        mService.setLockCredential(credential, type, null, quality, userId);
         assertVerifyCredentials(userId, credential, type, -1);
     }
 
     private void testChangeCredentials(int userId, String newCredential, int newType,
-            String oldCredential, int oldType) throws RemoteException {
+            String oldCredential, int oldType, int quality) throws RemoteException {
         final long sid = 1234;
         initializeStorageWithCredential(userId, oldCredential, oldType, sid);
-        mService.setLockCredential(newCredential, newType, oldCredential, userId);
+        mService.setLockCredential(newCredential, newType, oldCredential, quality, userId);
         assertVerifyCredentials(userId, newCredential, newType, sid);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 449a54c..a0578c9 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -22,8 +22,6 @@
 
 import android.app.NotificationManager;
 import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.pm.UserInfo;
 import android.database.sqlite.SQLiteDatabase;
 import android.os.FileUtils;
@@ -33,6 +31,8 @@
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
+import com.android.server.locksettings.LockSettingsStorage.PersistentData;
+
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -43,11 +43,14 @@
  * runtest frameworks-services -c com.android.server.locksettings.LockSettingsStorageTests
  */
 public class LockSettingsStorageTests extends AndroidTestCase {
+    private static final int SOME_USER_ID = 1034;
     private final byte[] PASSWORD_0 = "thepassword0".getBytes();
     private final byte[] PASSWORD_1 = "password1".getBytes();
     private final byte[] PATTERN_0 = "123654".getBytes();
     private final byte[] PATTERN_1 = "147852369".getBytes();
 
+    public static final byte[] PAYLOAD = new byte[] {1, 2, -1, -2, 33};
+
     LockSettingsStorage mStorage;
     File mStorageDir;
 
@@ -342,6 +345,83 @@
         assertEquals(null, mStorage.readSyntheticPasswordState(10, 1234L, "state"));
     }
 
+    public void testPersistentData_serializeUnserialize() {
+        byte[] serialized = PersistentData.toBytes(PersistentData.TYPE_GATEKEEPER, SOME_USER_ID,
+                DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, PAYLOAD);
+        PersistentData deserialized = PersistentData.fromBytes(serialized);
+
+        assertEquals(PersistentData.TYPE_GATEKEEPER, deserialized.type);
+        assertEquals(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, deserialized.qualityForUi);
+        assertArrayEquals(PAYLOAD, deserialized.payload);
+    }
+
+    public void testPersistentData_unserializeNull() {
+        PersistentData deserialized = PersistentData.fromBytes(null);
+        assertSame(PersistentData.NONE, deserialized);
+    }
+
+    public void testPersistentData_unserializeEmptyArray() {
+        PersistentData deserialized = PersistentData.fromBytes(new byte[0]);
+        assertSame(PersistentData.NONE, deserialized);
+    }
+
+    public void testPersistentData_unserialize_version1() {
+        // This test ensures that we can read serialized VERSION_1 PersistentData even if we change
+        // the wire format in the future.
+        byte[] serializedVersion1 = new byte[] {
+                1, /* PersistentData.VERSION_1 */
+                2, /* PersistentData.TYPE_SP */
+                0x00, 0x00, 0x04, 0x0A,  /* SOME_USER_ID */
+                0x00, 0x03, 0x00, 0x00,  /* PASSWORD_NUMERIC_COMPLEX */
+                1, 2, -1, -2, 33, /* PAYLOAD */
+        };
+        PersistentData deserialized = PersistentData.fromBytes(serializedVersion1);
+        assertEquals(PersistentData.TYPE_SP, deserialized.type);
+        assertEquals(SOME_USER_ID, deserialized.userId);
+        assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
+                deserialized.qualityForUi);
+        assertArrayEquals(PAYLOAD, deserialized.payload);
+
+        // Make sure the constants we use on the wire do not change.
+        assertEquals(0, PersistentData.TYPE_NONE);
+        assertEquals(1, PersistentData.TYPE_GATEKEEPER);
+        assertEquals(2, PersistentData.TYPE_SP);
+        assertEquals(3, PersistentData.TYPE_SP_WEAVER);
+    }
+
+    public void testCredentialHash_serializeUnserialize() {
+        byte[] serialized = CredentialHash.create(
+                PAYLOAD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD).toBytes();
+        CredentialHash deserialized = CredentialHash.fromBytes(serialized);
+
+        assertEquals(CredentialHash.VERSION_GATEKEEPER, deserialized.version);
+        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type);
+        assertArrayEquals(PAYLOAD, deserialized.hash);
+        assertFalse(deserialized.isBaseZeroPattern);
+    }
+
+    public void testCredentialHash_unserialize_versionGatekeeper() {
+        // This test ensures that we can read serialized VERSION_GATEKEEPER CredentialHashes
+        // even if we change the wire format in the future.
+        byte[] serialized = new byte[] {
+                1, /* VERSION_GATEKEEPER */
+                2, /* CREDENTIAL_TYPE_PASSWORD */
+                0, 0, 0, 5, /* hash length */
+                1, 2, -1, -2, 33, /* hash */
+        };
+        CredentialHash deserialized = CredentialHash.fromBytes(serialized);
+
+        assertEquals(CredentialHash.VERSION_GATEKEEPER, deserialized.version);
+        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type);
+        assertArrayEquals(PAYLOAD, deserialized.hash);
+        assertFalse(deserialized.isBaseZeroPattern);
+
+        // Make sure the constants we use on the wire do not change.
+        assertEquals(-1, LockPatternUtils.CREDENTIAL_TYPE_NONE);
+        assertEquals(1, LockPatternUtils.CREDENTIAL_TYPE_PATTERN);
+        assertEquals(2, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
+    }
+
     private static void assertArrayEquals(byte[] expected, byte[] actual) {
         if (!Arrays.equals(expected, actual)) {
             fail("expected:<" + Arrays.toString(expected) +
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockGateKeeperService.java b/services/tests/servicestests/src/com/android/server/locksettings/MockGateKeeperService.java
index eefd361..b89c1d1 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockGateKeeperService.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockGateKeeperService.java
@@ -173,6 +173,10 @@
     }
 
     @Override
+    public void reportDeviceSetupComplete() throws RemoteException {
+    }
+
+    @Override
     public long getSecureUserId(int userId) throws RemoteException {
         if (sidMap.containsKey(userId)) {
             return sidMap.get(userId);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
index ddef5dc..d7468c2 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java
@@ -17,6 +17,7 @@
 
 import android.hardware.weaver.V1_0.IWeaver;
 import android.os.RemoteException;
+import android.os.UserManager;
 import android.util.ArrayMap;
 
 import junit.framework.AssertionFailedError;
@@ -35,8 +36,8 @@
     private IWeaver mWeaverService;
 
     public MockSyntheticPasswordManager(LockSettingsStorage storage,
-            MockGateKeeperService gatekeeper) {
-        super(storage);
+            MockGateKeeperService gatekeeper, UserManager userManager) {
+        super(storage, userManager);
         mGateKeeper = gatekeeper;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 0d35385..ba4ff33 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -16,9 +16,15 @@
 
 package com.android.server.locksettings;
 
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
 
+import android.app.admin.DevicePolicyManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
 
@@ -26,6 +32,7 @@
 import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
+import com.android.server.locksettings.SyntheticPasswordManager.PasswordData;
 
 
 /**
@@ -33,6 +40,9 @@
  */
 public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
 
+    public static final byte[] PAYLOAD = new byte[] {1, 2, -1, -2, 55};
+    public static final byte[] PAYLOAD2 = new byte[] {2, 3, -2, -3, 44, 1};
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -47,11 +57,13 @@
         final int USER_ID = 10;
         final String PASSWORD = "user-password";
         final String BADPASSWORD = "bad-password";
-        MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mStorage, mGateKeeperService);
+        MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mStorage,
+                mGateKeeperService, mUserManager);
         AuthenticationToken authToken = manager.newSyntheticPasswordAndSid(mGateKeeperService, null,
                 null, USER_ID);
         long handle = manager.createPasswordBasedSyntheticPassword(mGateKeeperService, PASSWORD,
-                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, authToken, USER_ID);
+                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, authToken, PASSWORD_QUALITY_ALPHABETIC,
+                USER_ID);
 
         AuthenticationResult result = manager.unwrapPasswordBasedSyntheticPassword(mGateKeeperService, handle, PASSWORD, USER_ID);
         assertEquals(result.authToken.deriveKeyStorePassword(), authToken.deriveKeyStorePassword());
@@ -76,7 +88,8 @@
         final String PASSWORD = "testPasswordMigration-password";
 
         disableSyntheticPassword(PRIMARY_USER_ID);
-        mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
+        mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
         long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
         final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
         enableSyntheticPassword(PRIMARY_USER_ID);
@@ -94,7 +107,11 @@
 
     private void initializeCredentialUnderSP(String password, int userId) throws RemoteException {
         enableSyntheticPassword(userId);
-        mService.setLockCredential(password, password != null ? LockPatternUtils.CREDENTIAL_TYPE_PASSWORD : LockPatternUtils.CREDENTIAL_TYPE_NONE, null, userId);
+        int quality = password != null ? PASSWORD_QUALITY_ALPHABETIC
+                : PASSWORD_QUALITY_UNSPECIFIED;
+        int type = password != null ? LockPatternUtils.CREDENTIAL_TYPE_PASSWORD
+                : LockPatternUtils.CREDENTIAL_TYPE_NONE;
+        mService.setLockCredential(password, type, null, quality, userId);
     }
 
     public void testSyntheticPasswordChangeCredential() throws RemoteException {
@@ -103,7 +120,8 @@
 
         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
         long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
-        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD, PRIMARY_USER_ID);
+        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD,
+                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
         mGateKeeperService.clearSecureUserId(PRIMARY_USER_ID);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
                 mService.verifyCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
@@ -129,11 +147,13 @@
         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
         long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
         // clear password
-        mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD, PRIMARY_USER_ID);
+        mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, PASSWORD,
+                PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
         assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
 
         // set a new password
-        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
+        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
                 mService.verifyCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
         assertNotSame(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
@@ -146,11 +166,13 @@
         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
         long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
         // clear password
-        mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
+        mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null,
+                PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
         assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
 
         // set a new password
-        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
+        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
                 mService.verifyCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
         assertNotSame(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
@@ -163,7 +185,8 @@
         initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
         long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
         // Untrusted change password
-        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
+        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
         assertNotSame(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
         assertNotSame(sid ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
 
@@ -177,7 +200,8 @@
         final String UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd";
         disableSyntheticPassword(PRIMARY_USER_ID);
         disableSyntheticPassword(MANAGED_PROFILE_USER_ID);
-        mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
+        mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
         mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
         final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
         final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
@@ -207,8 +231,10 @@
     public void testManagedProfileSeparateChallengeMigration() throws RemoteException {
         final String primaryPassword = "testManagedProfileSeparateChallengeMigration-primary";
         final String profilePassword = "testManagedProfileSeparateChallengeMigration-profile";
-        mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PRIMARY_USER_ID);
-        mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, MANAGED_PROFILE_USER_ID);
+        mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+        mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+                PASSWORD_QUALITY_ALPHABETIC, MANAGED_PROFILE_USER_ID);
         final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
         final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
         final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID);
@@ -251,7 +277,8 @@
         mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode();
         assertTrue(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
-        mService.setLockCredentialWithToken(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, TOKEN.getBytes(), PRIMARY_USER_ID);
+        mService.setLockCredentialWithToken(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+                handle, TOKEN.getBytes(), PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
 
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
                 mService.verifyCredential(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID).getResponseCode());
@@ -271,8 +298,10 @@
         mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode();
         assertTrue(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
-        mService.setLockCredentialWithToken(null, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, TOKEN.getBytes(), PRIMARY_USER_ID);
-        mService.setLockCredentialWithToken(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, TOKEN.getBytes(), PRIMARY_USER_ID);
+        mService.setLockCredentialWithToken(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, handle,
+                TOKEN.getBytes(), PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID);
+        mService.setLockCredentialWithToken(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+                handle, TOKEN.getBytes(), PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
 
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
                 mService.verifyCredential(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID).getResponseCode());
@@ -293,9 +322,11 @@
         mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode();
         assertTrue(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
-        mService.setLockCredential(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, PASSWORD, PRIMARY_USER_ID);
+        mService.setLockCredential(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, PASSWORD,
+                PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID);
 
-        mService.setLockCredentialWithToken(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, TOKEN.getBytes(), PRIMARY_USER_ID);
+        mService.setLockCredentialWithToken(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+                handle, TOKEN.getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
 
         assertEquals(VerifyCredentialResponse.RESPONSE_OK,
                 mService.verifyCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode());
@@ -326,7 +357,7 @@
         // Set up pre-SP user password
         disableSyntheticPassword(PRIMARY_USER_ID);
         mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
-                PRIMARY_USER_ID);
+                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
         enableSyntheticPassword(PRIMARY_USER_ID);
 
         long handle = mService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID);
@@ -340,10 +371,51 @@
         assertTrue(mService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
     }
 
+    public void testPasswordData_serializeDeserialize() {
+        PasswordData data = new PasswordData();
+        data.scryptN = 11;
+        data.scryptR = 22;
+        data.scryptP = 33;
+        data.passwordType = CREDENTIAL_TYPE_PASSWORD;
+        data.salt = PAYLOAD;
+        data.passwordHandle = PAYLOAD2;
+
+        PasswordData deserialized = PasswordData.fromBytes(data.toBytes());
+
+        assertEquals(11, deserialized.scryptN);
+        assertEquals(22, deserialized.scryptR);
+        assertEquals(33, deserialized.scryptP);
+        assertEquals(CREDENTIAL_TYPE_PASSWORD, deserialized.passwordType);
+        assertArrayEquals(PAYLOAD, deserialized.salt);
+        assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
+    }
+
+    public void testPasswordData_deserialize() {
+        // Test that we can deserialize existing PasswordData and don't inadvertently change the
+        // wire format.
+        byte[] serialized = new byte[] {
+                0, 0, 0, 2, /* CREDENTIAL_TYPE_PASSWORD */
+                11, /* scryptN */
+                22, /* scryptR */
+                33, /* scryptP */
+                0, 0, 0, 5, /* salt.length */
+                1, 2, -1, -2, 55, /* salt */
+                0, 0, 0, 6, /* passwordHandle.length */
+                2, 3, -2, -3, 44, 1, /* passwordHandle */
+        };
+        PasswordData deserialized = PasswordData.fromBytes(serialized);
+
+        assertEquals(11, deserialized.scryptN);
+        assertEquals(22, deserialized.scryptR);
+        assertEquals(33, deserialized.scryptP);
+        assertEquals(CREDENTIAL_TYPE_PASSWORD, deserialized.passwordType);
+        assertArrayEquals(PAYLOAD, deserialized.salt);
+        assertArrayEquals(PAYLOAD2, deserialized.passwordHandle);
+    }
+
     // b/34600579
     //TODO: add non-migration work profile case, and unify/un-unify transition.
     //TODO: test token after user resets password
     //TODO: test token based reset after unified work challenge
     //TODO: test clear password after unified work challenge
 }
-
diff --git a/services/tests/servicestests/src/com/android/server/retaildemo/PreloadAppsInstallerTest.java b/services/tests/servicestests/src/com/android/server/retaildemo/PreloadAppsInstallerTest.java
index 6706969..cf27a50 100644
--- a/services/tests/servicestests/src/com/android/server/retaildemo/PreloadAppsInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/retaildemo/PreloadAppsInstallerTest.java
@@ -39,7 +39,9 @@
 import com.android.internal.util.test.FakeSettingsProvider;
 
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -65,6 +67,12 @@
 
     private PreloadAppsInstaller mInstaller;
 
+    @BeforeClass
+    @AfterClass
+    public static void clearSettingsProvider() {
+        FakeSettingsProvider.clearSettingsProvider();
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
diff --git a/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java b/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
index c9946c3..a93f643 100644
--- a/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
@@ -71,7 +71,9 @@
 import com.android.server.retaildemo.RetailDemoModeService.Injector;
 
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -85,6 +87,7 @@
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
+@SmallTest
 public class RetailDemoModeServiceTest {
     private static final int TEST_DEMO_USER = 111;
     private static final long SETUP_COMPLETE_TIMEOUT_MS = 2000; // 2 sec
@@ -111,6 +114,12 @@
     private RetailDemoModeService mService;
     private TestInjector mInjector;
 
+    @BeforeClass
+    @AfterClass
+    public static void clearSettingsProvider() {
+        FakeSettingsProvider.clearSettingsProvider();
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
diff --git a/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java b/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java
index d2a4484..8828988 100644
--- a/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java
@@ -23,12 +23,14 @@
     private TestUtils() {
     }
 
+    public interface ExceptionRunnable {
+        void run() throws Exception;
+    }
+
     public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
-            String expectedExceptionMessageRegex, Runnable r) {
+            String expectedExceptionMessageRegex, ExceptionRunnable r) {
         try {
             r.run();
-            Assert.fail("Expected exception type " + expectedExceptionType.getName()
-                    + " was not thrown");
         } catch (Throwable e) {
             Assert.assertTrue(
                     "Expected exception type was " + expectedExceptionType.getName()
@@ -37,6 +39,9 @@
             if (expectedExceptionMessageRegex != null) {
                 MoreAsserts.assertContainsRegex(expectedExceptionMessageRegex, e.getMessage());
             }
+            return; // Pass.
         }
+        Assert.fail("Expected exception type " + expectedExceptionType.getName()
+                + " was not thrown");
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 5f51898..c809c32 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import android.util.MergedConfiguration;
+import android.view.WindowManager;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -204,4 +206,20 @@
         assertEquals(mediaChild, windows.pollFirst());
         assertTrue(windows.isEmpty());
     }
+
+    @Test
+    public void testPrepareWindowToDisplayDuringRelayout() throws Exception {
+        testPrepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
+        testPrepareWindowToDisplayDuringRelayout(true /*wasVisible*/);
+    }
+
+    private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
+        final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
+        root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
+        root.mTurnOnScreen = false;
+
+        root.prepareWindowToDisplayDuringRelayout(new MergedConfiguration(),
+                wasVisible /*wasVisible*/);
+        assertTrue(root.mTurnOnScreen);
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 16b73d5..562443f 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -62,6 +62,8 @@
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.storage.CacheQuotaStrategy;
 
+import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 
 public class StorageStatsService extends IStorageStatsManager.Stub {
@@ -181,29 +183,42 @@
     public long getFreeBytes(String volumeUuid, String callingPackage) {
         // NOTE: No permissions required
 
-        long cacheBytes = 0;
         final long token = Binder.clearCallingIdentity();
         try {
+            final File path;
+            try {
+                path = mStorage.findPathForUuid(volumeUuid);
+            } catch (FileNotFoundException e) {
+                throw new ParcelableException(e);
+            }
+
+            // Free space is usable bytes plus any cached data that we're
+            // willing to automatically clear. To avoid user confusion, this
+            // logic should be kept in sync with getAllocatableBytes().
             if (isQuotaSupported(volumeUuid, callingPackage)) {
-                for (UserInfo user : mUser.getUsers()) {
-                    final StorageStats stats = queryStatsForUser(volumeUuid, user.id, null);
-                    cacheBytes += stats.cacheBytes;
-                }
+                final long cacheTotal = getCacheBytes(volumeUuid, callingPackage);
+                final long cacheReserved = mStorage.getStorageCacheBytes(path);
+                final long cacheClearable = Math.max(0, cacheTotal - cacheReserved);
+
+                return path.getUsableSpace() + cacheClearable;
+            } else {
+                return path.getUsableSpace();
             }
         } finally {
             Binder.restoreCallingIdentity(token);
         }
+    }
 
-        if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
-            return Environment.getDataDirectory().getFreeSpace() + cacheBytes;
-        } else {
-            final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid);
-            if (vol == null) {
-                throw new ParcelableException(
-                        new IOException("Failed to find storage device for UUID " + volumeUuid));
-            }
-            return vol.getPath().getFreeSpace() + cacheBytes;
+    @Override
+    public long getCacheBytes(String volumeUuid, String callingPackage) {
+        enforcePermission(Binder.getCallingUid(), callingPackage);
+
+        long cacheBytes = 0;
+        for (UserInfo user : mUser.getUsers()) {
+            final StorageStats stats = queryStatsForUser(volumeUuid, user.id, null);
+            cacheBytes += stats.cacheBytes;
         }
+        return cacheBytes;
     }
 
     @Override
diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java
index 6754426..e90a63c 100644
--- a/telephony/java/android/telephony/MbmsStreamingManager.java
+++ b/telephony/java/android/telephony/MbmsStreamingManager.java
@@ -156,7 +156,6 @@
      *
      * This may throw an {@link MbmsException} containing one of the following errors:
      * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
-     * {@link MbmsException#ERROR_UNKNOWN_REMOTE_EXCEPTION}
      * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
      * {@link MbmsException#ERROR_SERVICE_LOST}
      *
@@ -174,12 +173,10 @@
             if (returnCode != MbmsException.SUCCESS) {
                 throw new MbmsException(returnCode);
             }
-        } catch (DeadObjectException e) {
+        } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService = null;
             throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
-        } catch (RemoteException e) {
-            throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
         }
     }
 
@@ -191,7 +188,6 @@
      *
      * May throw an {@link MbmsException} containing any of the following error codes:
      * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
-     * {@link MbmsException#ERROR_UNKNOWN_REMOTE_EXCEPTION}
      * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
      * {@link MbmsException#ERROR_SERVICE_LOST}
      *
@@ -211,12 +207,10 @@
             if (returnCode != MbmsException.SUCCESS) {
                 throw new MbmsException(returnCode);
             }
-        } catch (DeadObjectException e) {
+        } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService = null;
             throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
-        } catch (RemoteException e) {
-            throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
         }
 
         return new StreamingService(
@@ -280,11 +274,7 @@
             } catch (RemoteException e) {
                 mService = null;
                 Log.e(LOG_TAG, "Service died before initialization");
-                if (e instanceof DeadObjectException) {
-                    throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
-                } else {
-                    throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
-                }
+                throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
             }
         }
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 233a8d1..37c86a7 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -31,16 +31,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
+import android.net.NetworkStats;
 import android.net.Uri;
 import android.os.BatteryStats;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -72,11 +67,6 @@
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -6607,20 +6597,21 @@
     /**
      * Get aggregated video call data usage since boot.
      * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
-     * @return total data usage in bytes
+     *
+     * @param perUidStats True if requesting data usage per uid, otherwise overall usage.
+     * @return Snapshot of video call data usage
      * @hide
      */
-    public long getVtDataUsage() {
-
+    public NetworkStats getVtDataUsage(boolean perUidStats) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.getVtDataUsage();
+                return service.getVtDataUsage(getSubId(), perUidStats);
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling getVtDataUsage", e);
+            Log.e(TAG, "Error calling ITelephony#getVtDataUsage", e);
         }
-        return 0;
+        return null;
     }
 
     /**
diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java
index ac14c4f..6b90592 100644
--- a/telephony/java/android/telephony/mbms/MbmsException.java
+++ b/telephony/java/android/telephony/mbms/MbmsException.java
@@ -22,7 +22,7 @@
     public static final int ERROR_NO_SERVICE_INSTALLED = 1;
     public static final int ERROR_MULTIPLE_SERVICES_INSTALLED = 2;
     public static final int ERROR_BIND_TIMEOUT_OR_FAILURE = 3;
-    public static final int ERROR_UNKNOWN_REMOTE_EXCEPTION = 4;
+    public static final int ERROR_UNABLE_TO_INITIALIZE = 4;
     public static final int ERROR_ALREADY_INITIALIZED = 5;
     public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 6;
     public static final int ERROR_MIDDLEWARE_NOT_BOUND = 7;
@@ -30,6 +30,12 @@
     public static final int ERROR_STREAM_ALREADY_STARTED = 9;
     public static final int ERROR_END_OF_SESSION = 10;
     public static final int ERROR_SERVICE_LOST = 11;
+    public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 12;
+    public static final int ERROR_IN_E911 = 13;
+    public static final int ERROR_OUT_OF_MEMORY = 14;
+    public static final int ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE = 15;
+    public static final int ERROR_UNABLE_TO_READ_SIM = 16;
+    public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 17;
 
     private final int mErrorCode;
 
diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java
index 745cb98..85ba625 100644
--- a/telephony/java/android/telephony/mbms/StreamingService.java
+++ b/telephony/java/android/telephony/mbms/StreamingService.java
@@ -55,8 +55,7 @@
     /**
      * Retreive the Uri used to play this stream.
      *
-     * This may throw a {@link MbmsException} with the error codes
-     * {@link MbmsException#ERROR_UNKNOWN_REMOTE_EXCEPTION} or
+     * This may throw a {@link MbmsException} with the error code
      * {@link MbmsException#ERROR_SERVICE_LOST}
      *
      * @return The {@link Uri} to pass to the streaming client.
@@ -68,13 +67,10 @@
 
         try {
             return mService.getPlaybackUri(mAppName, mSubscriptionId, mServiceInfo.getServiceId());
-        } catch (DeadObjectException e) {
+        } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService = null;
             throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Caught remote exception calling getPlaybackUri: " + e);
-            throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
         }
     }
 
@@ -88,7 +84,6 @@
     /**
      * Stop streaming this service.
      * This may throw a {@link MbmsException} with the error code
-     * {@link MbmsException#ERROR_UNKNOWN_REMOTE_EXCEPTION} or
      * {@link MbmsException#ERROR_SERVICE_LOST}
      */
     public void stopStreaming() throws MbmsException {
@@ -98,13 +93,10 @@
 
         try {
             mService.stopStreaming(mAppName, mSubscriptionId, mServiceInfo.getServiceId());
-        } catch (DeadObjectException e) {
+        } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService = null;
             throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Caught remote exception calling stopStreaming: " + e);
-            throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
         }
     }
 
@@ -115,13 +107,10 @@
 
         try {
             mService.disposeStream(mAppName, mSubscriptionId, mServiceInfo.getServiceId());
-        } catch (DeadObjectException e) {
+        } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService = null;
             throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Caught remote exception calling dispose: " + e);
-            throw new MbmsException(MbmsException.ERROR_UNKNOWN_REMOTE_EXCEPTION);
         }
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index c53f5d3..ab7c5e7 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -22,6 +22,7 @@
 import android.os.IBinder;
 import android.os.Messenger;
 import android.os.ResultReceiver;
+import android.net.NetworkStats;
 import android.net.Uri;
 import android.service.carrier.CarrierIdentifier;
 import android.telecom.PhoneAccount;
@@ -1295,10 +1296,12 @@
     /**
      * Get aggregated video call data usage since boot.
      * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
-     * @return total data usage in bytes
+     *
+     * @param perUidStats True if requesting data usage per uid, otherwise overall usage.
+     * @return Snapshot of video call data usage
      * @hide
      */
-    long getVtDataUsage();
+    NetworkStats getVtDataUsage(int subId, boolean perUidStats);
 
     /**
      * Policy control of data connection. Usually used when data limit is passed.
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index bfc2d72..ebad81c 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -149,6 +149,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public void reloadSharedPreferences() {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
         throw new UnsupportedOperationException();
diff --git a/tests/net/java/android/net/nsd/NsdManagerTest.java b/tests/net/java/android/net/nsd/NsdManagerTest.java
index 063cd5dc..adf6998 100644
--- a/tests/net/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/net/java/android/net/nsd/NsdManagerTest.java
@@ -333,7 +333,7 @@
     public static class MockServiceHandler extends Handler {
         public Context mContext;
         public AsyncChannel chan;
-        public Message lastMessage;
+        public volatile Message lastMessage;
 
         MockServiceHandler(Looper looper, Context context) {
             super(looper);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index fb9d0c3..198ddc6 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2415,8 +2415,7 @@
     }
 
     @Ignore // This test has instrinsic chances of spurious failures: ignore for continuous testing.
-    @SmallTest
-    public void testRequestBenchmark() throws Exception {
+    public void benchmarkRequestRegistrationAndCallbackDispatch() throws Exception {
         // TODO: turn this unit test into a real benchmarking test.
         // Benchmarks connecting and switching performance in the presence of a large number of
         // NetworkRequests.
@@ -2426,9 +2425,9 @@
         //    and NUM_REQUESTS onAvailable callbacks to fire.
         // See how long it took.
         final int NUM_REQUESTS = 90;
-        final int REGISTER_TIME_LIMIT_MS = 180;
-        final int CONNECT_TIME_LIMIT_MS = 50;
-        final int SWITCH_TIME_LIMIT_MS = 50;
+        final int REGISTER_TIME_LIMIT_MS = 200;
+        final int CONNECT_TIME_LIMIT_MS = 60;
+        final int SWITCH_TIME_LIMIT_MS = 60;
         final int UNREGISTER_TIME_LIMIT_MS = 20;
 
         final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index ddceea2..27be135 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -31,6 +31,7 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.res.Resources;
+import android.net.util.SharedLog;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.TelephonyManager;
@@ -47,6 +48,7 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class TetheringConfigurationTest {
+    private final SharedLog mLog = new SharedLog("TetheringConfigurationTest");
     @Mock private Context mContext;
     @Mock private TelephonyManager mTelephonyManager;
     @Mock private Resources mResources;
@@ -91,7 +93,7 @@
         mHasTelephonyManager = true;
         when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_REQUIRED);
 
-        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext);
+        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
         assertTrue(cfg.isDunRequired);
         assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
         assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
@@ -107,7 +109,7 @@
         mHasTelephonyManager = true;
         when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_NOT_REQUIRED);
 
-        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext);
+        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
         assertFalse(cfg.isDunRequired);
         assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
         assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
@@ -123,7 +125,7 @@
         mHasTelephonyManager = false;
         when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
 
-        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext);
+        final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
         assertTrue(cfg.isDunRequired);
         assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
         // Just to prove we haven't clobbered Wi-Fi:
diff --git a/tools/aapt2/Format.proto b/tools/aapt2/Format.proto
index 0917129..870b735 100644
--- a/tools/aapt2/Format.proto
+++ b/tools/aapt2/Format.proto
@@ -69,6 +69,7 @@
 	optional Visibility visibility = 1;
 	optional Source source = 2;
 	optional string comment = 3;
+	optional bool allow_new = 4;
 }
 
 message Entry {
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 10244da..bc13378 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -92,14 +92,14 @@
   Source source;
   ResourceId id;
   Maybe<SymbolState> symbol_state;
+  bool allow_new = false;
   std::string comment;
   std::unique_ptr<Value> value;
   std::list<ParsedResource> child_resources;
 };
 
 // Recursively adds resources to the ResourceTable.
-static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag,
-                                ParsedResource* res) {
+static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
   StringPiece trimmed_comment = util::TrimWhitespace(res->comment);
   if (trimmed_comment.size() != res->comment.size()) {
     // Only if there was a change do we re-assign.
@@ -111,6 +111,7 @@
     symbol.state = res->symbol_state.value();
     symbol.source = res->source;
     symbol.comment = res->comment;
+    symbol.allow_new = res->allow_new;
     if (!table->SetSymbolState(res->name, res->id, symbol, diag)) {
       return false;
     }
@@ -121,8 +122,8 @@
     res->value->SetComment(std::move(res->comment));
     res->value->SetSource(std::move(res->source));
 
-    if (!table->AddResource(res->name, res->id, res->config, res->product,
-                            std::move(res->value), diag)) {
+    if (!table->AddResource(res->name, res->id, res->config, res->product, std::move(res->value),
+                            diag)) {
       return false;
     }
   }
@@ -849,6 +850,7 @@
                                       ParsedResource* out_resource) {
   if (ParseSymbolImpl(parser, out_resource)) {
     out_resource->symbol_state = SymbolState::kUndefined;
+    out_resource->allow_new = true;
     return true;
   }
   return false;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index faa6607..e3abde6 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -777,8 +777,7 @@
   ASSERT_FALSE(TestParse(input));
 }
 
-TEST_F(ResourceParserTest,
-       AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
+TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
   std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF";
   ASSERT_TRUE(TestParse(input));
 
@@ -788,6 +787,7 @@
   const ResourceEntry* entry = result.value().entry;
   ASSERT_NE(nullptr, entry);
   EXPECT_EQ(SymbolState::kUndefined, entry->symbol_status.state);
+  EXPECT_TRUE(entry->symbol_status.allow_new);
 }
 
 TEST_F(ResourceParserTest, ParseItemElementWithFormat) {
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 1947628..168004f 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -440,8 +440,7 @@
   return true;
 }
 
-bool ResourceTable::SetSymbolState(const ResourceNameRef& name,
-                                   const ResourceId& res_id,
+bool ResourceTable::SetSymbolState(const ResourceNameRef& name, const ResourceId& res_id,
                                    const Symbol& symbol, IDiagnostics* diag) {
   return SetSymbolStateImpl(name, res_id, symbol, ValidateName, diag);
 }
@@ -489,8 +488,7 @@
     diag->Error(DiagMessage(symbol.source)
                 << "trying to add resource '" << name << "' with ID " << res_id
                 << " but resource already has ID "
-                << ResourceId(package->id.value(), type->id.value(),
-                              entry->id.value()));
+                << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
     return false;
   }
 
@@ -505,6 +503,11 @@
     type->symbol_status.state = SymbolState::kPublic;
   }
 
+  if (symbol.allow_new) {
+    // This symbol can be added as a new resource when merging (if it belongs to an overlay).
+    entry->symbol_status.allow_new = true;
+  }
+
   if (symbol.state == SymbolState::kUndefined &&
       entry->symbol_status.state != SymbolState::kUndefined) {
     // We can't undefine a symbol (remove its visibility). Ignore.
@@ -517,7 +520,10 @@
     return true;
   }
 
-  entry->symbol_status = std::move(symbol);
+  // This symbol definition takes precedence, replace.
+  entry->symbol_status.state = symbol.state;
+  entry->symbol_status.source = symbol.source;
+  entry->symbol_status.comment = symbol.comment;
   return true;
 }
 
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index b032121..4295d06 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -50,6 +50,10 @@
 struct Symbol {
   SymbolState state = SymbolState::kUndefined;
   Source source;
+
+  // Whether this entry (originating from an overlay) can be added as a new resource.
+  bool allow_new = false;
+
   std::string comment;
 };
 
@@ -223,8 +227,7 @@
   bool SetSymbolState(const ResourceNameRef& name, const ResourceId& res_id,
                       const Symbol& symbol, IDiagnostics* diag);
 
-  bool SetSymbolStateAllowMangled(const ResourceNameRef& name,
-                                  const ResourceId& res_id,
+  bool SetSymbolStateAllowMangled(const ResourceNameRef& name, const ResourceId& res_id,
                                   const Symbol& symbol, IDiagnostics* diag);
 
   struct SearchResult {
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index cc1cd5c..b64cd8c 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -480,7 +480,8 @@
 
   {
     std::string content;
-    if (!android::base::ReadFileToString(path_data.source.path, &content)) {
+    if (!android::base::ReadFileToString(path_data.source.path, &content,
+                                         true /*follow_symlinks*/)) {
       context->GetDiagnostics()->Error(DiagMessage(path_data.source)
                                        << "failed to open file: "
                                        << android::base::SystemErrorCodeToString(errno));
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 24a687c..dd4b2ba 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -632,7 +632,7 @@
 static bool LoadStableIdMap(IDiagnostics* diag, const std::string& path,
                             std::unordered_map<ResourceName, ResourceId>* out_id_map) {
   std::string content;
-  if (!android::base::ReadFileToString(path, &content)) {
+  if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) {
     diag->Error(DiagMessage(path) << "failed reading stable ID file");
     return false;
   }
diff --git a/tools/aapt2/integration-tests/SymlinkTest/Android.mk b/tools/aapt2/integration-tests/SymlinkTest/Android.mk
new file mode 100644
index 0000000..902fc65
--- /dev/null
+++ b/tools/aapt2/integration-tests/SymlinkTest/Android.mk
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_PACKAGE_NAME := AaptSymlinkTest
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/integration-tests/SymlinkTest/AndroidManifest.xml b/tools/aapt2/integration-tests/SymlinkTest/AndroidManifest.xml
new file mode 100644
index 0000000..abbfa53
--- /dev/null
+++ b/tools/aapt2/integration-tests/SymlinkTest/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.aapt.symlinktest" />
diff --git a/tools/aapt2/integration-tests/SymlinkTest/res/drawable/white_3x3.9.png b/tools/aapt2/integration-tests/SymlinkTest/res/drawable/white_3x3.9.png
new file mode 120000
index 0000000..a7e8e2f
--- /dev/null
+++ b/tools/aapt2/integration-tests/SymlinkTest/res/drawable/white_3x3.9.png
@@ -0,0 +1 @@
+../../targets/white_3x3.9.png
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/SymlinkTest/res/layout/layout.xml b/tools/aapt2/integration-tests/SymlinkTest/res/layout/layout.xml
new file mode 120000
index 0000000..29c30e1
--- /dev/null
+++ b/tools/aapt2/integration-tests/SymlinkTest/res/layout/layout.xml
@@ -0,0 +1 @@
+../../targets/layout.xml
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/SymlinkTest/res/values/values.xml b/tools/aapt2/integration-tests/SymlinkTest/res/values/values.xml
new file mode 120000
index 0000000..554bb71
--- /dev/null
+++ b/tools/aapt2/integration-tests/SymlinkTest/res/values/values.xml
@@ -0,0 +1 @@
+../../targets/values.xml
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/SymlinkTest/targets/layout.xml b/tools/aapt2/integration-tests/SymlinkTest/targets/layout.xml
new file mode 100644
index 0000000..e5835ed
--- /dev/null
+++ b/tools/aapt2/integration-tests/SymlinkTest/targets/layout.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
diff --git a/tools/aapt2/integration-tests/SymlinkTest/targets/values.xml b/tools/aapt2/integration-tests/SymlinkTest/targets/values.xml
new file mode 100644
index 0000000..5b02843
--- /dev/null
+++ b/tools/aapt2/integration-tests/SymlinkTest/targets/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <bool name="foo">true</bool>
+</resources>
diff --git a/tools/aapt2/integration-tests/SymlinkTest/targets/white_3x3.9.png b/tools/aapt2/integration-tests/SymlinkTest/targets/white_3x3.9.png
new file mode 100644
index 0000000..1a3731b
--- /dev/null
+++ b/tools/aapt2/integration-tests/SymlinkTest/targets/white_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 9311091..cce750ac 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -243,8 +243,7 @@
   bool error = false;
 
   for (auto& src_type : src_package->types) {
-    ResourceTableType* dst_type =
-        master_package_->FindOrCreateType(src_type->type);
+    ResourceTableType* dst_type = master_package_->FindOrCreateType(src_type->type);
     if (!MergeType(context_, src, dst_type, src_type.get())) {
       error = true;
       continue;
@@ -253,27 +252,24 @@
     for (auto& src_entry : src_type->entries) {
       std::string entry_name = src_entry->name;
       if (mangle_package) {
-        entry_name =
-            NameMangler::MangleEntry(src_package->name, src_entry->name);
+        entry_name = NameMangler::MangleEntry(src_package->name, src_entry->name);
       }
 
       ResourceEntry* dst_entry;
-      if (allow_new_resources) {
+      if (allow_new_resources || src_entry->symbol_status.allow_new) {
         dst_entry = dst_type->FindOrCreateEntry(entry_name);
       } else {
         dst_entry = dst_type->FindEntry(entry_name);
       }
 
-      const ResourceNameRef res_name(src_package->name, src_type->type,
-                                     src_entry->name);
+      const ResourceNameRef res_name(src_package->name, src_type->type, src_entry->name);
 
       if (!dst_entry) {
-        context_->GetDiagnostics()->Error(
-            DiagMessage(src) << "resource " << res_name
-                             << " does not override an existing resource");
-        context_->GetDiagnostics()->Note(
-            DiagMessage(src) << "define an <add-resource> tag or use "
-                             << "--auto-add-overlay");
+        context_->GetDiagnostics()->Error(DiagMessage(src)
+                                          << "resource " << res_name
+                                          << " does not override an existing resource");
+        context_->GetDiagnostics()->Note(DiagMessage(src) << "define an <add-resource> tag or use "
+                                                          << "--auto-add-overlay");
         error = true;
         continue;
       }
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 742f5a7..147d857 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -248,18 +248,18 @@
 
 TEST_F(TableMergerTest, MergeAddResourceFromOverlay) {
   std::unique_ptr<ResourceTable> table_a =
-      test::ResourceTableBuilder()
-          .SetPackageId("", 0x7f)
-          .SetSymbolState("bool/foo", {}, SymbolState::kUndefined)
-          .Build();
+      test::ResourceTableBuilder().SetPackageId("", 0x7f).Build();
   std::unique_ptr<ResourceTable> table_b =
       test::ResourceTableBuilder()
           .SetPackageId("", 0x7f)
+          .SetSymbolState("bool/foo", {}, SymbolState::kUndefined, true /*allow new overlay*/)
           .AddValue("bool/foo", ResourceUtils::TryParseBool("true"))
           .Build();
 
   ResourceTable final_table;
-  TableMerger merger(context_.get(), &final_table, TableMergerOptions{});
+  TableMergerOptions options;
+  options.auto_add_overlay = false;
+  TableMerger merger(context_.get(), &final_table, options);
 
   ASSERT_TRUE(merger.Merge({}, table_a.get()));
   ASSERT_TRUE(merger.MergeOverlay({}, table_b.get()));
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index d93d495..1d0041b 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -32,8 +32,7 @@
  public:
   using ValueVisitor::Visit;
 
-  explicit ReferenceIdToNameVisitor(
-      const std::map<ResourceId, ResourceNameRef>* mapping)
+  explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping)
       : mapping_(mapping) {
     CHECK(mapping_ != nullptr);
   }
@@ -75,13 +74,11 @@
 
     std::map<ResourceId, ResourceNameRef> idIndex;
 
-    ResourceTablePackage* pkg =
-        table->CreatePackage(pbPackage.package_name(), id);
+    ResourceTablePackage* pkg = table->CreatePackage(pbPackage.package_name(), id);
     for (const pb::Type& pbType : pbPackage.types()) {
       const ResourceType* resType = ParseResourceType(pbType.name());
       if (!resType) {
-        diag_->Error(DiagMessage(source_) << "unknown type '" << pbType.name()
-                                          << "'");
+        diag_->Error(DiagMessage(source_) << "unknown type '" << pbType.name() << "'");
         return {};
       }
 
@@ -95,21 +92,20 @@
         if (pbEntry.has_symbol_status()) {
           const pb::SymbolStatus& pbStatus = pbEntry.symbol_status();
           if (pbStatus.has_source()) {
-            DeserializeSourceFromPb(pbStatus.source(), *source_pool_,
-                                    &entry->symbol_status.source);
+            DeserializeSourceFromPb(pbStatus.source(), *source_pool_, &entry->symbol_status.source);
           }
 
           if (pbStatus.has_comment()) {
             entry->symbol_status.comment = pbStatus.comment();
           }
 
-          SymbolState visibility =
-              DeserializeVisibilityFromPb(pbStatus.visibility());
+          entry->symbol_status.allow_new = pbStatus.allow_new();
+
+          SymbolState visibility = DeserializeVisibilityFromPb(pbStatus.visibility());
           entry->symbol_status.state = visibility;
 
           if (visibility == SymbolState::kPublic) {
-            // This is a public symbol, we must encode the ID now if there is
-            // one.
+            // This is a public symbol, we must encode the ID now if there is one.
             if (pbEntry.has_id()) {
               entry->id = static_cast<uint16_t>(pbEntry.id());
             }
@@ -142,16 +138,15 @@
             return {};
           }
 
-          ResourceConfigValue* configValue =
-              entry->FindOrCreateValue(config, pbConfig.product());
+          ResourceConfigValue* configValue = entry->FindOrCreateValue(config, pbConfig.product());
           if (configValue->value) {
             // Duplicate config.
             diag_->Error(DiagMessage(source_) << "duplicate configuration");
             return {};
           }
 
-          configValue->value = DeserializeValueFromPb(
-              pbConfigValue.value(), config, &table->string_pool);
+          configValue->value =
+              DeserializeValueFromPb(pbConfigValue.value(), config, &table->string_pool);
           if (!configValue->value) {
             return {};
           }
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
index 7230b55..de10bb8 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -250,19 +250,16 @@
 
         // Write the SymbolStatus struct.
         pb::SymbolStatus* pb_status = pb_entry->mutable_symbol_status();
-        pb_status->set_visibility(
-            SerializeVisibilityToPb(entry->symbol_status.state));
-        SerializeSourceToPb(entry->symbol_status.source, &source_pool,
-                            pb_status->mutable_source());
+        pb_status->set_visibility(SerializeVisibilityToPb(entry->symbol_status.state));
+        SerializeSourceToPb(entry->symbol_status.source, &source_pool, pb_status->mutable_source());
         pb_status->set_comment(entry->symbol_status.comment);
+        pb_status->set_allow_new(entry->symbol_status.allow_new);
 
         for (auto& config_value : entry->values) {
           pb::ConfigValue* pb_config_value = pb_entry->add_config_values();
-          SerializeConfig(config_value->config,
-                          pb_config_value->mutable_config());
+          SerializeConfig(config_value->config, pb_config_value->mutable_config());
           if (!config_value->product.empty()) {
-            pb_config_value->mutable_config()->set_product(
-                config_value->product);
+            pb_config_value->mutable_config()->set_product(config_value->product);
           }
 
           pb::Value* pb_value = pb_config_value->mutable_value();
diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp
index fdd5197..e6ce6d3 100644
--- a/tools/aapt2/proto/TableProtoSerializer_test.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp
@@ -28,12 +28,11 @@
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.a", 0x7f)
-          .AddFileReference("com.app.a:layout/main", ResourceId(0x7f020000),
-                            "res/layout/main.xml")
-          .AddReference("com.app.a:layout/other", ResourceId(0x7f020001),
-                        "com.app.a:layout/main")
+          .AddFileReference("com.app.a:layout/main", ResourceId(0x7f020000), "res/layout/main.xml")
+          .AddReference("com.app.a:layout/other", ResourceId(0x7f020001), "com.app.a:layout/main")
           .AddString("com.app.a:string/text", {}, "hi")
           .AddValue("com.app.a:id/foo", {}, util::make_unique<Id>())
+          .SetSymbolState("com.app.a:bool/foo", {}, SymbolState::kUndefined, true /*allow_new*/)
           .Build();
 
   Symbol public_symbol;
@@ -94,21 +93,23 @@
   EXPECT_EQ(SymbolState::kPublic, result.value().type->symbol_status.state);
   EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state);
 
+  result = new_table->FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
+  ASSERT_TRUE(result);
+  EXPECT_EQ(SymbolState::kUndefined, result.value().entry->symbol_status.state);
+  EXPECT_TRUE(result.value().entry->symbol_status.allow_new);
+
   // Find the product-dependent values
   BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
-      new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"),
-      "");
+      new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "");
   ASSERT_NE(nullptr, prim);
   EXPECT_EQ(123u, prim->value.data);
 
   prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
-      new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"),
-      "tablet");
+      new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet");
   ASSERT_NE(nullptr, prim);
   EXPECT_EQ(321u, prim->value.data);
 
-  Reference* actual_ref =
-      test::GetValue<Reference>(new_table.get(), "com.app.a:layout/abc");
+  Reference* actual_ref = test::GetValue<Reference>(new_table.get(), "com.app.a:layout/abc");
   ASSERT_NE(nullptr, actual_ref);
   AAPT_ASSERT_TRUE(actual_ref->name);
   AAPT_ASSERT_TRUE(actual_ref->id);
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index 7e28ae7b..0290e30 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -1,5 +1,10 @@
 # Android Asset Packaging Tool 2.0 (AAPT2) release notes
 
+## Version 2.17
+### `aapt2 compile ...`
+- Fixed an issue where symlinks would not be followed when compiling PNGs. (bug 62144459)
+- Fixed issue where overlays that declared `<add-resource>` did not compile. (bug 38355988)
+
 ## Version 2.16
 ### `aapt2 link ...`
 - Versioning of XML files is more intelligent, using a small set of rules to degrade
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 6888cf3..02acedb 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -117,12 +117,12 @@
   }
 
   ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
-                                       SymbolState state) {
+                                       SymbolState state, bool allow_new = false) {
     ResourceName res_name = ParseNameOrDie(name);
     Symbol symbol;
     symbol.state = state;
-    CHECK(table_->SetSymbolStateAllowMangled(res_name, id, symbol,
-                                             &diagnostics_));
+    symbol.allow_new = allow_new;
+    CHECK(table_->SetSymbolStateAllowMangled(res_name, id, symbol, &diagnostics_));
     return *this;
   }
 
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index d10351b..1bf2594 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -181,12 +181,13 @@
   return std::move(filemap);
 }
 
-bool AppendArgsFromFile(const StringPiece& path,
-                        std::vector<std::string>* out_arglist,
+bool AppendArgsFromFile(const StringPiece& path, std::vector<std::string>* out_arglist,
                         std::string* out_error) {
   std::string contents;
-  if (!android::base::ReadFileToString(path.to_string(), &contents)) {
-    if (out_error) *out_error = "failed to read argument-list file";
+  if (!android::base::ReadFileToString(path.to_string(), &contents, true /*follow_symlinks*/)) {
+    if (out_error) {
+      *out_error = "failed to read argument-list file";
+    }
     return false;
   }
 
diff --git a/tools/fonts/add_additional_fonts.py b/tools/fonts/add_additional_fonts.py
new file mode 100644
index 0000000..bf4af2b
--- /dev/null
+++ b/tools/fonts/add_additional_fonts.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import sys
+
+def main(argv):
+    original_file = 'frameworks/base/data/fonts/fonts.xml'
+
+    if len(argv) == 3:
+        output_file_path = argv[1]
+        override_file_path = argv[2]
+    else:
+        raise ValueError("Wrong number of arguments %s" % len(argv))
+
+    fallbackPlaceholderFound = False
+    with open(original_file, 'r') as input_file:
+        with open(output_file_path, 'w') as output_file:
+            for line in input_file:
+                # If we've found the spot to add additional fonts, add them.
+                if line.strip() == '<!-- fallback fonts -->':
+                    fallbackPlaceholderFound = True
+                    with open(override_file_path) as override_file:
+                        for override_line in override_file:
+                            output_file.write(override_line)
+                output_file.write(line)
+    if not fallbackPlaceholderFound:
+        raise ValueError('<!-- fallback fonts --> not found in source file: %s' % original_file)
+
+if __name__ == '__main__':
+    main(sys.argv)
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index c0475ee..c6ad4c2 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -3,7 +3,6 @@
 import collections
 import copy
 import glob
-import itertools
 from os import path
 import sys
 from xml.etree import ElementTree
@@ -574,8 +573,8 @@
         all_sequences.add(sequence)
         sequence_pieces.update(sequence)
         if _emoji_sequences.get(sequence, None) == 'Emoji_Tag_Sequence':
-            # Add reverse of all emoji ZWJ sequences, which are added to the fonts
-            # as a workaround to get the sequences work in RTL text.
+            # Add reverse of all emoji ZWJ sequences, which are added to the
+            # fonts as a workaround to get the sequences work in RTL text.
             # TODO: test if these are actually needed by Minikin/HarfBuzz.
             reversed_seq = reverse_emoji(sequence)
             all_sequences.add(reversed_seq)
@@ -630,12 +629,26 @@
         if record.name in ['sans-serif', 'sans-serif-condensed']:
             font = open_font(record.font)
             assert font['head'].yMax == 2163 and font['head'].yMin == -555, (
-                'yMax and yMin of %s do not match expected values.' % (record.font,))
+                'yMax and yMin of %s do not match expected values.' % (
+                record.font,))
 
-        if record.name in ['sans-serif', 'sans-serif-condensed', 'serif', 'monospace']:
+        if record.name in ['sans-serif', 'sans-serif-condensed',
+                           'serif', 'monospace']:
             font = open_font(record.font)
-            assert font['hhea'].ascent == 1900 and font['hhea'].descent == -500, (
-                'ascent and descent of %s do not match expected values.' % (record.font,))
+            assert (font['hhea'].ascent == 1900 and
+                    font['hhea'].descent == -500), (
+                        'ascent and descent of %s do not match expected '
+                        'values.' % (record.font,))
+
+
+def check_cjk_punctuation():
+    cjk_scripts = {'Hans', 'Hant', 'Jpan', 'Kore'}
+    cjk_punctuation = range(0x3000, 0x301F + 1)
+    for record in _fallback_chain:
+        if record.scripts.intersection(cjk_scripts):
+            # CJK font seen. Stop checking the rest of the fonts.
+            break
+        assert_font_supports_none_of_chars(record.font, cjk_punctuation)
 
 
 def main():
@@ -651,6 +664,8 @@
     hyphens_dir = path.join(target_out, 'usr', 'hyphen-data')
     check_hyphens(hyphens_dir)
 
+    check_cjk_punctuation()
+
     check_emoji = sys.argv[2]
     if check_emoji == 'true':
         ucd_path = sys.argv[3]
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index cfc1fd2..164b7b0 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -160,6 +160,8 @@
      *
      * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
      * components will be launched.
+     *
+     * @hide
      */
     public static final String ACTION_PASSPOINT_ICON = "android.net.wifi.action.PASSPOINT_ICON";
     /**
@@ -167,6 +169,8 @@
      * String representation.
      *
      * Retrieve with {@link android.content.Intent#getLongExtra(String, long)}.
+     *
+     * @hide
      */
     public static final String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG";
     /**
@@ -174,12 +178,16 @@
      *
      * Retrieve with {@link android.content.Intent#getParcelableExtra(String)} and cast into
      * {@link android.graphics.drawable.Icon}.
+     *
+     * @hide
      */
     public static final String EXTRA_ICON = "android.net.wifi.extra.ICON";
     /**
      * Name of a file.
      *
      * Retrieve with {@link android.content.Intent#getStringExtra(String)}.
+     *
+     * @hide
      */
     public static final String EXTRA_FILENAME = "android.net.wifi.extra.FILENAME";
 
@@ -195,6 +203,7 @@
      * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
      * components will be launched.
      *
+     * @hide
      */
     public static final String ACTION_PASSPOINT_OSU_PROVIDERS_LIST =
             "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST";
@@ -202,6 +211,8 @@
      * Raw binary data of an ANQP (Access Network Query Protocol) element.
      *
      * Retrieve with {@link android.content.Intent#getByteArrayExtra(String)}.
+     *
+     * @hide
      */
     public static final String EXTRA_ANQP_ELEMENT_DATA =
             "android.net.wifi.extra.ANQP_ELEMENT_DATA";
@@ -220,6 +231,7 @@
      * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
      * components will be launched.
      *
+     * @hide
      */
     public static final String ACTION_PASSPOINT_DEAUTH_IMMINENT =
             "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT";
@@ -228,18 +240,24 @@
      * {@code true} for ESS.
      *
      * Retrieve with {@link android.content.Intent#getBooleanExtra(String, boolean)}.
+     *
+     * @hide
      */
     public static final String EXTRA_ESS = "android.net.wifi.extra.ESS";
     /**
      * Delay in seconds.
      *
      * Retrieve with {@link android.content.Intent#getIntExtra(String, int)}.
+     *
+     * @hide
      */
     public static final String EXTRA_DELAY = "android.net.wifi.extra.DELAY";
     /**
      * String representation of an URL.
      *
      * Retrieve with {@link android.content.Intent#getStringExtra(String)}.
+     *
+     * @hide
      */
     public static final String EXTRA_URL = "android.net.wifi.extra.URL";
 
@@ -254,8 +272,10 @@
      *
      * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE
      *
-     ** <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
+     * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
      * components will be launched.
+     *
+     * @hide
      */
     public static final String ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION =
             "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION";
@@ -265,6 +285,8 @@
      * 1 - SOAP XML SPP
      *
      * Retrieve with {@link android.content.Intent#getIntExtra(String, int)}.
+     *
+     * @hide
      */
     public static final String EXTRA_SUBSCRIPTION_REMEDIATION_METHOD =
             "android.net.wifi.extra.SUBSCRIPTION_REMEDIATION_METHOD";
@@ -1122,6 +1144,8 @@
      *
      * @param bssid The BSSID of the AP
      * @param fileName Name of the icon file (remote file) to query from the AP
+     *
+     * @hide
      */
     public void queryPasspointIcon(long bssid, String fileName) {
         try {
@@ -1778,25 +1802,25 @@
     }
 
     /**
-     * Start AccessPoint mode with the specified
-     * configuration. If the radio is already running in
-     * AP mode, update the new configuration
-     * Note that starting in access point mode disables station
-     * mode operation
+     * This call will be deprecated and removed in an upcoming release.  It is no longer used to
+     * start WiFi Tethering.  Please use {@link ConnectivityManager#startTethering(int, boolean,
+     * ConnectivityManager#OnStartTetheringCallback)} if
+     * the caller has proper permissions.  Callers can also use the LocalOnlyHotspot feature for a
+     * hotspot capable of communicating with co-located devices {@link
+     * WifiManager#startLocalOnlyHotspot(LocalOnlyHotspotCallback)}.
+     *
      * @param wifiConfig SSID, security and channel details as
      *        part of WifiConfiguration
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
+     * @return {@code false}
      *
      * @hide
      */
     @SystemApi
     public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
-        try {
-            mService.setWifiApEnabled(wifiConfig, enabled);
-            return true;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        String packageName = mContext.getOpPackageName();
+
+        Log.w(TAG, packageName + " attempted call to setWifiApEnabled: enabled = " + enabled);
+        return false;
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 84ac118..b235ccc7 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -778,4 +778,13 @@
         verify(mWifiService).stopWatchLocalOnlyHotspot();
     }
 
+    /**
+     * Verify that calls to setWifiApEnabled return false.
+     */
+    @Test
+    public void testSetWifiApEnabledReturnsFalse() throws Exception {
+        assertFalse(mWifiManager.setWifiApEnabled(null, true));
+        assertFalse(mWifiManager.setWifiApEnabled(null, false));
+        verify(mWifiService, never()).setWifiApEnabled(any(), anyBoolean());
+    }
 }