Merge "Don't crash when presented with non-roundRect clipping outline." into oc-dev
diff --git a/api/current.txt b/api/current.txt
index 6023aab..ac97b54 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26513,7 +26513,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);
@@ -26526,29 +26525,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";
@@ -33907,7 +33894,6 @@
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI";
-    field public static final java.lang.String EXTRA_SENDER_ACCOUNT_HASH = "android.provider.extra.SENDER_ACCOUNT_HASH";
     field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT";
     field public static final java.lang.String METADATA_ACCOUNT_TYPE = "android.provider.account_type";
     field public static final java.lang.String METADATA_MIMETYPE = "android.provider.mimetype";
diff --git a/api/system-current.txt b/api/system-current.txt
index 67b6e82..a2c2771 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -29038,7 +29038,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);
@@ -29055,10 +29054,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
@@ -29066,14 +29061,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";
@@ -29081,10 +29070,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";
@@ -36821,7 +36808,6 @@
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI";
-    field public static final java.lang.String EXTRA_SENDER_ACCOUNT_HASH = "android.provider.extra.SENDER_ACCOUNT_HASH";
     field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT";
     field public static final java.lang.String METADATA_ACCOUNT_TYPE = "android.provider.account_type";
     field public static final java.lang.String METADATA_MIMETYPE = "android.provider.mimetype";
diff --git a/api/test-current.txt b/api/test-current.txt
index d3cb4c9..1cad48e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -26622,7 +26622,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);
@@ -26635,29 +26634,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";
@@ -34044,7 +34031,6 @@
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI";
-    field public static final java.lang.String EXTRA_SENDER_ACCOUNT_HASH = "android.provider.extra.SENDER_ACCOUNT_HASH";
     field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT";
     field public static final java.lang.String METADATA_ACCOUNT_TYPE = "android.provider.account_type";
     field public static final java.lang.String METADATA_MIMETYPE = "android.provider.mimetype";
@@ -45743,6 +45729,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();
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/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/AlertDialog.java b/core/java/android/app/AlertDialog.java
index b7f1068..a44bd03 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -25,6 +25,7 @@
 import android.annotation.StyleRes;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.res.ResourceId;
 import android.database.Cursor;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -204,7 +205,7 @@
         mAlert = AlertController.create(getContext(), this, getWindow());
     }
 
-    static int resolveDialogTheme(Context context, int themeResId) {
+    static @StyleRes int resolveDialogTheme(Context context, @StyleRes int themeResId) {
         if (themeResId == THEME_TRADITIONAL) {
             return R.style.Theme_Dialog_Alert;
         } else if (themeResId == THEME_HOLO_DARK) {
@@ -215,7 +216,7 @@
             return R.style.Theme_DeviceDefault_Dialog_Alert;
         } else if (themeResId == THEME_DEVICE_DEFAULT_LIGHT) {
             return R.style.Theme_DeviceDefault_Light_Dialog_Alert;
-        } else if (Integer.compareUnsigned(themeResId, 0x01000000) >= 0) {
+        } else if (ResourceId.isValid(themeResId)) {
             // start of real resource IDs.
             return themeResId;
         } else {
@@ -450,7 +451,7 @@
          * @param context the parent context
          */
         public Builder(Context context) {
-            this(context, resolveDialogTheme(context, 0));
+            this(context, resolveDialogTheme(context, ResourceId.ID_NULL));
         }
 
         /**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 525b151..e5c4208 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2108,7 +2108,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();
         }
@@ -2117,7 +2117,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/Dialog.java b/core/java/android/app/Dialog.java
index 943c572..b162cb1 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -34,6 +34,7 @@
 import android.content.DialogInterface;
 import android.content.res.Configuration;
 import android.content.pm.ApplicationInfo;
+import android.content.res.ResourceId;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
@@ -169,7 +170,7 @@
 
     Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
         if (createContextThemeWrapper) {
-            if (themeResId == 0) {
+            if (themeResId == ResourceId.ID_NULL) {
                 final TypedValue outValue = new TypedValue();
                 context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
                 themeResId = outValue.resourceId;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3ed174b..c626ae9 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
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/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index c613d97..c99a1e4 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1682,11 +1682,6 @@
         }
 
         @Override
-        public void setAutofillId(@NonNull ViewStructure parent, int virtualId) {
-            setAutofillId(parent.getAutofillId(), virtualId);
-        }
-
-        @Override
         public ViewStructure newChild(int index) {
             ViewNode node = new ViewNode();
             mNode.mChildren[index] = node;
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/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 52fa8a6..fd1b0e0 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.res.ResourceId;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
@@ -368,7 +369,7 @@
         try {
             Resources resources = context.getPackageManager().getResourcesForApplication(
                     providerInfo.applicationInfo);
-            if (resourceId != 0) {
+            if (ResourceId.isValid(resourceId)) {
                 if (density < 0) {
                     density = 0;
                 }
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/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 7aaf453..2ebfa8f 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -360,7 +360,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
@@ -384,7 +384,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/content/res/ResourceId.java b/core/java/android/content/res/ResourceId.java
new file mode 100644
index 0000000..adb9cf1
--- /dev/null
+++ b/core/java/android/content/res/ResourceId.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package android.content.res;
+
+import android.annotation.AnyRes;
+
+/**
+ * Provides a set of utility methods for dealing with Resource IDs.
+ * @hide
+ */
+public final class ResourceId {
+
+    /**
+     * The {@code null} resource ID.
+     */
+    public static final @AnyRes int ID_NULL = 0;
+
+    /**
+     * Checks whether the integer {@code id} is a valid resource ID, as generated by AAPT.
+     * <p>Note that a negative integer is not necessarily an invalid resource ID, and custom
+     * validations that compare the {@code id} against {@code 0} are incorrect.</p>
+     * @param id The integer to validate.
+     * @return {@code true} if the integer is a valid resource ID.
+     */
+    public static boolean isValid(@AnyRes int id) {
+        // With the introduction of packages with IDs > 0x7f, resource IDs can be negative when
+        // represented as a signed Java int. Some legacy code assumes -1 is an invalid resource ID,
+        // despite the existing documentation.
+        return id != -1 && (id & 0xff000000) != 0 && (id & 0x00ff0000) != 0;
+    }
+}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index e525ab3..60226d5 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -151,7 +151,7 @@
     /** @hide */
     public static int selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo,
             int dark, int deviceDefault) {
-        if (curTheme != 0) {
+        if (curTheme != ResourceId.ID_NULL) {
             return curTheme;
         }
         if (targetSdkVersion < Build.VERSION_CODES.HONEYCOMB) {
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index b3adf82..23591c7 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -36,6 +36,7 @@
 import android.graphics.Typeface;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableContainer;
 import android.icu.text.PluralRules;
 import android.os.Build;
 import android.os.LocaleList;
@@ -590,6 +591,7 @@
             }
 
             Drawable dr;
+            boolean needsNewDrawableAfterCache = false;
             if (cs != null) {
                 dr = cs.newDrawable(wrapper);
             } else if (isColorDrawable) {
@@ -597,6 +599,12 @@
             } else {
                 dr = loadDrawableForCookie(wrapper, value, id, density, null);
             }
+            // DrawableContainer' constant state has drawables instances. In order to leave the
+            // constant state intact in the cache, we need to create a new DrawableContainer after
+            // added to cache.
+            if (dr instanceof DrawableContainer)  {
+                needsNewDrawableAfterCache = true;
+            }
 
             // Determine if the drawable has unresolved theme attributes. If it
             // does, we'll need to apply a theme and store it in a theme-specific
@@ -615,6 +623,12 @@
                 dr.setChangingConfigurations(value.changingConfigurations);
                 if (useCache) {
                     cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
+                    if (needsNewDrawableAfterCache) {
+                        Drawable.ConstantState state = dr.getConstantState();
+                        if (state != null) {
+                            dr = state.newDrawable(wrapper);
+                        }
+                    }
                 }
             }
 
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/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 5408e13..70ef035 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8915,15 +8915,11 @@
          * ambiguous then the activity should prompt the user for the recipient to send the message
          * to.
          * <p>
-         * Voice Assistant may provide additional information to messaging app about which account
-         * to use for sending a message by populating {@link #EXTRA_SENDER_ACCOUNT_HASH}.
-         * <p>
          * Output: nothing
          *
          * @see #EXTRA_RECIPIENT_CONTACT_URI
          * @see #EXTRA_RECIPIENT_CONTACT_CHAT_ID
          * @see #EXTRA_RECIPIENT_CONTACT_NAME
-         * @see #EXTRA_SENDER_ACCOUNT_HASH
          * @see #METADATA_ACCOUNT_TYPE
          * @see #METADATA_MIMETYPE
          */
@@ -8982,21 +8978,6 @@
                 "android.provider.extra.RECIPIENT_CONTACT_NAME";
 
         /**
-         * This optional extra specifies the hash of the account that should be used by messaging
-         * app for sending voice message with {@link #ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS}. The
-         * value of this extra is a {@code String} and should be the value of {@link
-         * android.accounts.Account#hashCode()} for some account returned by {@link
-         * android.accounts.AccountManager#getAccounts()}.
-         * <p>
-         * If the extra is not specified, the app can decide which account to use.
-         * <p>
-         * If the account specified in the extra cannot be used for any reason (account missing, not
-         * usable by the app, etc), the message should not be sent.
-         */
-        public static final String EXTRA_SENDER_ACCOUNT_HASH =
-                "android.provider.extra.SENDER_ACCOUNT_HASH";
-
-        /**
          * A string associated with an {@link #ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS} activity
          * describing {@link RawContacts#ACCOUNT_TYPE} for the corresponding Contacts Provider
          * implementation.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3cd54b8..6a17ed1 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9817,13 +9817,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/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java
index adf4938..480abc1 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -1593,7 +1593,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 +1720,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 +1785,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..d2dcb56 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 11b55a8..a69b813 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";
 
@@ -1189,7 +1229,8 @@
     public @interface AutofillFlags {}
 
     /**
-     * Flag requesting you to add views not-important for autofill to the assist data.
+     * Flag requesting you to add views that are marked as not important for autofill
+     * (see {@link #setImportantForAutofill(int)}) to a {@link ViewStructure}.
      */
     public static final int AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x1;
 
@@ -7486,7 +7527,7 @@
      * <li>Call {@link AutofillManager#commit()} when the autofill context
      * of the view structure changed and you want the current autofill interaction if such
      * to be commited.
-     * <li>Call {@link AutofillManager#cancel()} ()} when the autofill context
+     * <li>Call {@link AutofillManager#cancel()} when the autofill context
      * of the view structure changed and you want the current autofill interaction if such
      * to be cancelled.
      * <li> The {@code left} and {@code top} values set in
@@ -7582,9 +7623,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
      */
@@ -9249,8 +9294,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
@@ -19808,18 +19871,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;
     }
 
@@ -19831,7 +19899,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 28ded55..a720aae 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;
@@ -7343,6 +7339,14 @@
         return false;
     }
 
+
+    private void reportNextDraw() {
+        if (mReportNextDraw == false) {
+            drawPending();
+        }
+        mReportNextDraw = true;
+    }
+
     /**
      * Force the window to report its next draw.
      * <p>
@@ -7352,7 +7356,7 @@
      * @hide
      */
     public void setReportNextDraw() {
-        mReportNextDraw = true;
+        reportNextDraw();
         invalidate();
     }
 
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 6bdc9ff..ddfe697 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -304,13 +304,6 @@
     public abstract void setAutofillId(@NonNull AutofillId parentId, int virtualId);
 
     /**
-     * @deprecated - use {@link #setAutofillId(AutofillId, int)} instead
-     * @hide
-     */
-    @Deprecated
-    public abstract void setAutofillId(@NonNull ViewStructure parent, int virtualId);
-
-    /**
      * Sets the {@link View#getAutofillType()} that can be used to autofill this node.
      */
     public abstract void setAutofillType(@View.AutofillType int type);
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/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index d8b316e..a2aff93 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -388,9 +388,10 @@
     /**
      * Explicitly requests a new autofill context.
      *
-     * <p>Normally, the autofill context is automatically started when autofillable views are
-     * focused, but this method should be used in the cases where it must be explicitly requested,
-     * like a view that provides a contextual menu allowing users to autofill the activity.
+     * <p>Normally, the autofill context is automatically started if necessary when
+     * {@link #notifyViewEntered(View)} is called, but this method should be used in the
+     * cases where it must be explicitly started. For example, when the view offers an AUTOFILL
+     * option on its contextual overflow menu, and the user selects it.
      *
      * @param view view requesting the new autofill context.
      */
@@ -401,16 +402,29 @@
     /**
      * Explicitly requests a new autofill context for virtual views.
      *
-     * <p>Normally, the autofill context is automatically started when autofillable views are
-     * focused, but this method should be used in the cases where it must be explicitly requested,
-     * like a virtual view that provides a contextual menu allowing users to autofill the activity.
+     * <p>Normally, the autofill context is automatically started if necessary when
+     * {@link #notifyViewEntered(View, int, Rect)} is called, but this method should be used in the
+     * cases where it must be explicitly started. For example, when the virtual view offers an
+     * AUTOFILL option on its contextual overflow menu, and the user selects it.
      *
-     * @param view the {@link View} whose descendant is the virtual view.
-     * @param childId id identifying the virtual child inside the view.
-     * @param bounds child boundaries, relative to the top window.
+     * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
+     * parent view uses {@code bounds} to draw the virtual view inside its Canvas,
+     * the absolute bounds could be calculated by:
+     *
+     * <pre class="prettyprint">
+     *   int offset[] = new int[2];
+     *   getLocationOnScreen(offset);
+     *   Rect absBounds = new Rect(bounds.left + offset[0],
+     *       bounds.top + offset[1],
+     *       bounds.right + offset[0], bounds.bottom + offset[1]);
+     * </pre>
+     *
+     * @param view the virtual view parent.
+     * @param virtualId id identifying the virtual child inside the parent view.
+     * @param absBounds absolute boundaries of the virtual view in the screen.
      */
-    public void requestAutofill(@NonNull View view, int childId, @NonNull Rect bounds) {
-        notifyViewEntered(view, childId, bounds, FLAG_MANUAL_REQUEST);
+    public void requestAutofill(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
+        notifyViewEntered(view, virtualId, absBounds, FLAG_MANUAL_REQUEST);
     }
 
     /**
@@ -502,15 +516,27 @@
     /**
      * Called when a virtual view that supports autofill is entered.
      *
-     * @param view the {@link View} whose descendant is the virtual view.
-     * @param childId id identifying the virtual child inside the view.
-     * @param bounds child boundaries, relative to the top window.
+     * <p>The virtual view boundaries must be absolute screen coordinates. For example, if the
+     * parent, non-virtual view uses {@code bounds} to draw the virtual view inside its Canvas,
+     * the absolute bounds could be calculated by:
+     *
+     * <pre class="prettyprint">
+     *   int offset[] = new int[2];
+     *   getLocationOnScreen(offset);
+     *   Rect absBounds = new Rect(bounds.left + offset[0],
+     *       bounds.top + offset[1],
+     *       bounds.right + offset[0], bounds.bottom + offset[1]);
+     * </pre>
+     *
+     * @param view the virtual view parent.
+     * @param virtualId id identifying the virtual child inside the parent view.
+     * @param absBounds absolute boundaries of the virtual view in the screen.
      */
-    public void notifyViewEntered(@NonNull View view, int childId, @NonNull Rect bounds) {
-        notifyViewEntered(view, childId, bounds, 0);
+    public void notifyViewEntered(@NonNull View view, int virtualId, @NonNull Rect absBounds) {
+        notifyViewEntered(view, virtualId, absBounds, 0);
     }
 
-    private void notifyViewEntered(View view, int childId, Rect bounds, int flags) {
+    private void notifyViewEntered(View view, int virtualId, Rect bounds, int flags) {
         if (!hasAutofillFeature()) {
             return;
         }
@@ -523,7 +549,7 @@
                     callback = mCallback;
                 }
             } else {
-                final AutofillId id = getAutofillId(view, childId);
+                final AutofillId id = getAutofillId(view, virtualId);
 
                 if (mSessionId == NO_SESSION) {
                     // Starts new session.
@@ -536,7 +562,7 @@
         }
 
         if (callback != null) {
-            callback.onAutofillEvent(view, childId,
+            callback.onAutofillEvent(view, virtualId,
                     AutofillCallback.EVENT_INPUT_UNAVAILABLE);
         }
     }
@@ -544,10 +570,10 @@
     /**
      * Called when a virtual view that supports autofill is exited.
      *
-     * @param view the {@link View} whose descendant is the virtual view.
-     * @param childId id identifying the virtual child inside the view.
+     * @param view the virtual view parent.
+     * @param virtualId id identifying the virtual child inside the parent view.
      */
-    public void notifyViewExited(@NonNull View view, int childId) {
+    public void notifyViewExited(@NonNull View view, int virtualId) {
         if (!hasAutofillFeature()) {
             return;
         }
@@ -555,7 +581,7 @@
             ensureServiceClientAddedIfNeededLocked();
 
             if (mEnabled && mSessionId != NO_SESSION) {
-                final AutofillId id = getAutofillId(view, childId);
+                final AutofillId id = getAutofillId(view, virtualId);
 
                 // Update focus on existing session.
                 updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
@@ -615,13 +641,13 @@
     }
 
     /**
-     * Called to indicate the value of an autofillable virtual {@link View} changed.
+     * Called to indicate the value of an autofillable virtual view has changed.
      *
-     * @param view the {@link View} whose descendant is the virtual view.
-     * @param childId id identifying the virtual child inside the parent view.
+     * @param view the virtual view parent.
+     * @param virtualId id identifying the virtual child inside the parent view.
      * @param value new value of the child.
      */
-    public void notifyValueChanged(View view, int childId, AutofillValue value) {
+    public void notifyValueChanged(View view, int virtualId, AutofillValue value) {
         if (!hasAutofillFeature()) {
             return;
         }
@@ -630,7 +656,7 @@
                 return;
             }
 
-            final AutofillId id = getAutofillId(view, childId);
+            final AutofillId id = getAutofillId(view, virtualId);
             updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
         }
     }
@@ -765,8 +791,8 @@
         return new AutofillId(view.getAccessibilityViewId());
     }
 
-    private static AutofillId getAutofillId(View parent, int childId) {
-        return new AutofillId(parent.getAccessibilityViewId(), childId);
+    private static AutofillId getAutofillId(View parent, int virtualId) {
+        return new AutofillId(parent.getAccessibilityViewId(), virtualId);
     }
 
     private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@@ -1470,11 +1496,12 @@
          * Called after a change in the autofill state associated with a virtual view.
          *
          * @param view parent view associated with the change.
-         * @param childId id identifying the virtual child inside the parent view.
+         * @param virtualId id identifying the virtual child inside the parent view.
          *
          * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
          */
-        public void onAutofillEvent(@NonNull View view, int childId, @AutofillEventType int event) {
+        public void onAutofillEvent(@NonNull View view, int virtualId,
+                @AutofillEventType int event) {
         }
     }
 
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index ecb25fe..dda5df6 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2654,19 +2654,23 @@
      *       {@link ViewStructure#setAutofillHints(String[])}.
      *   <li>The {@code type} attribute of {@code INPUT} tags maps to
      *       {@link ViewStructure#setInputType(int)}.
-     *   <li>The {@code value} attribute maps to {@link ViewStructure#setText(CharSequence)}.
+     *   <li>The {@code value} attribute of {@code INPUT} tags maps to
+     *       {@link ViewStructure#setText(CharSequence)}.
+     *   <li>If the view is editalbe, the {@link ViewStructure#setAutofillType(int)} and
+     *   {@link ViewStructure#setAutofillValue(AutofillValue)} must be set.
      *   <li>The {@code placeholder} attribute maps to {@link ViewStructure#setHint(CharSequence)}.
-     *   <li>{@link ViewStructure#setDataIsSensitive(boolean)} whould only be called with
-     *       {@code true} for form fields whose {@code value} attribute was not pre-loaded.
      *   <li>Other HTML attributes can be represented through
      *   {@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}.
      * </ol>
      *
+     * <p>It should also call {@code structure.setDataIsSensitive(false)} for fields whose value
+     * were not dynamically changed (for example, through Javascript).
+     *
      * <p>Example1: an HTML form with 2 fields for username and password.
      *
      * <pre class="prettyprint">
-     *    <input type="text" name="username" id="user" value="mr.sparkle" autocomplete="username" placeholder="Email or username">
-     *    <input type="password" name="password" id="pass" autocomplete="current-password" placeholder="Password">
+     *    &lt;input type="text" name="username" id="user" value="Type your username" autocomplete="username" placeholder="Email or username"&gt;
+     *    &lt;input type="password" name="password" id="pass" autocomplete="current-password" placeholder="Password"&gt;
      * </pre>
      *
      * <p>Would map to:
@@ -2674,38 +2678,40 @@
      * <pre class="prettyprint">
      *     int index = structure.addChildCount(2);
      *     ViewStructure username = structure.newChild(index);
-     *     username.setAutofillId(structure, 1); // id 1 - first child
+     *     username.setAutofillId(structure.getAutofillId(), 1); // id 1 - first child
      *     username.setClassName("input");
      *     username.setInputType("android.widget.EditText");
      *     username.setAutofillHints("username");
-     *     username.setHtmlInfo(child.newHtmlInfoBuilder("input")
+     *     username.setHtmlInfo(username.newHtmlInfoBuilder("input")
+     *         .addAttribute("type", "text")
      *         .addAttribute("name", "username")
      *         .addAttribute("id", "user")
      *         .build());
      *     username.setHint("Email or username");
      *     username.setAutofillType(View.AUTOFILL_TYPE_TEXT);
-     *     username.setAutofillValue(AutofillValue.forText("mr.sparkle"));
-     *     username.setText("mr.sparkle");
-     *     username.setDataIsSensitive(true); // Contains real username, which is sensitive
+     *     username.setAutofillValue(AutofillValue.forText("Type your username"));
+     *     username.setText("Type your username");
+     *     // Value of the field is not sensitive because it was not dynamically changed:
+     *     username.setDataIsSensitive(false);
      *
      *     ViewStructure password = structure.newChild(index + 1);
      *     username.setAutofillId(structure, 2); // id 2 - second child
      *     password.setInputType("android.widget.EditText");
      *     password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
      *     password.setAutofillHints("current-password");
-     *     password.setHtmlInfo(child.newHtmlInfoBuilder("input")
+     *     password.setHtmlInfo(password.newHtmlInfoBuilder("input")
+     *         .addAttribute("type", "password")
      *         .addAttribute("name", "password")
      *         .addAttribute("id", "pass")
      *         .build());
      *     password.setHint("Password");
      *     password.setAutofillType(View.AUTOFILL_TYPE_TEXT);
-     *     password.setDataIsSensitive(false); // Value is not set
      * </pre>
      *
      * <p>Example2: an IFRAME tag.
      *
      * <pre class="prettyprint">
-     *    <iframe src="https://example.com/login"/>
+     *    &lt;iframe src="https://example.com/login"/&gt;
      * </pre>
      *
      * <p>Would map to:
@@ -2713,8 +2719,9 @@
      * <pre class="prettyprint">
      *     int index = structure.addChildCount(1);
      *     ViewStructure iframe = structure.newChildFor(index);
-     *     iframe.setHtmlInfo(child.newHtmlInfoBuilder("iframe")
-     *         .addAttribute("url", "https://example.com/login")
+     *     iframe.setAutofillId(structure.getAutofillId(), 1);
+     *     iframe.setHtmlInfo(iframe.newHtmlInfoBuilder("iframe")
+     *         .addAttribute("src", "https://example.com/login")
      *         .build());
      * </pre>
      */
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/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/jni/Android.mk b/core/jni/Android.mk
index cab4758..eb11182 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -318,6 +318,13 @@
 #                        is not being compiled with that level. Remove once this has changed.
 LOCAL_CLANG_CFLAGS += -Wno-c++11-extensions
 
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+LOCAL_CFLAGS += -D__ANDROID_DEBUGGABLE__
+endif
+ifneq (,$(filter true, $(PRODUCT_FULL_TREBLE)))
+LOCAL_CFLAGS += -D__ANDROID_TREBLE__
+endif
+
 include $(BUILD_SHARED_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 369bb7f..785fd2c 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>
@@ -661,7 +661,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");
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 0e67d30..b171a7e 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1019,6 +1019,12 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+// This is the maximum possible size because the SkColorSpace must be
+// representable (and therefore serializable) using a matrix and numerical
+// transfer function.  If we allow more color space representations in the
+// framework, we may need to update this maximum size.
+static constexpr uint32_t kMaxColorSpaceSerializedBytes = 80;
+
 static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
     if (parcel == NULL) {
         SkDebugf("-------- unparcel parcel is NULL\n");
@@ -1035,7 +1041,17 @@
     if (kRGBA_F16_SkColorType == colorType) {
         colorSpace = SkColorSpace::MakeSRGBLinear();
     } else if (colorSpaceSize > 0) {
-        colorSpace = SkColorSpace::Deserialize(p->readInplace(colorSpaceSize), colorSpaceSize);
+        if (colorSpaceSize > kMaxColorSpaceSerializedBytes) {
+            ALOGD("Bitmap_createFromParcel: Serialized SkColorSpace is larger than expected: "
+                    "%d bytes\n", colorSpaceSize);
+        }
+
+        const void* data = p->readInplace(colorSpaceSize);
+        if (data) {
+            colorSpace = SkColorSpace::Deserialize(data, colorSpaceSize);
+        } else {
+            ALOGD("Bitmap_createFromParcel: Unable to read serialized SkColorSpace data\n");
+        }
     }
     const int         width = p->readInt32();
     const int         height = p->readInt32();
@@ -1178,6 +1194,11 @@
         size_t size = data->size();
         p->writeUint32(size);
         if (size > 0) {
+            if (size > kMaxColorSpaceSerializedBytes) {
+                ALOGD("Bitmap_writeToParcel: Serialized SkColorSpace is larger than expected: "
+                        "%zu bytes\n", size);
+            }
+
             p->write(data->data(), size);
         }
     } else {
diff --git a/core/jni/android_hardware_Radio.cpp b/core/jni/android_hardware_Radio.cpp
index b6b1ac7..18cf8ca 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>
 
@@ -955,7 +955,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_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index dcb2300..19f779f 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -23,6 +23,8 @@
 #include "android_os_HwParcel.h"
 #include "android_os_HwRemoteBinder.h"
 
+#include <cstring>
+
 #include <JNIHelp.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <android/hidl/base/1.0/IBase.h>
@@ -331,8 +333,19 @@
 
     IServiceManager::Transport transport = transportRet;
 
-    if (   transport != IServiceManager::Transport::EMPTY
-        && transport != IServiceManager::Transport::HWBINDER) {
+#ifdef __ANDROID_TREBLE__
+#ifdef __ANDROID_DEBUGGABLE__
+    const char* testingOverride = std::getenv("TREBLE_TESTING_OVERRIDE");
+    const bool vintfLegacy = (transport == IServiceManager::Transport::EMPTY)
+            && testingOverride && !strcmp(testingOverride, "true");
+#else // __ANDROID_TREBLE__ but not __ANDROID_DEBUGGABLE__
+    const bool vintfLegacy = false;
+#endif // __ANDROID_DEBUGGABLE__
+#else // not __ANDROID_TREBLE__
+    const bool vintfLegacy = (transport == IServiceManager::Transport::EMPTY);
+#endif // __ANDROID_TREBLE__";
+
+    if (transport != IServiceManager::Transport::HWBINDER && !vintfLegacy) {
         LOG(ERROR) << "service " << ifaceName << " declares transport method "
                    << toString(transport) << " but framework expects hwbinder.";
         signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */);
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/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8ed76de..f8044e2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1901,6 +1901,10 @@
                 android:description="@string/permdesc_useDataInBackground"
                 android:protectionLevel="normal" />
 
+    <!-- @hide Allows an application to set display offsets for the screen.
+         This permission is not available to third party applications. -->
+    <permission android:name="android.permission.SET_DISPLAY_OFFSET"
+        android:protectionLevel="signature|privileged" />
 
     <!-- ================================== -->
     <!-- Permissions affecting the system wallpaper -->
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/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 54d5285..d875ed4 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -334,7 +334,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/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/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 6b8a279..14bc555 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -36,14 +36,14 @@
  * session.
  */
 public final class AudioPlaybackConfiguration implements Parcelable {
-    private final static String TAG = new String("AudioPlaybackConfiguration");
+    private static final String TAG = new String("AudioPlaybackConfiguration");
 
-    private final static boolean DEBUG = false;
+    private static final boolean DEBUG = false;
 
     /** @hide */
-    public final static int PLAYER_PIID_INVALID = -1;
+    public static final int PLAYER_PIID_INVALID = -1;
     /** @hide */
-    public final static int PLAYER_UPID_INVALID = -1;
+    public static final int PLAYER_UPID_INVALID = -1;
 
     // information about the implementation
     /**
@@ -51,37 +51,62 @@
      * An unknown type of player
      */
     @SystemApi
-    public final static int PLAYER_TYPE_UNKNOWN = -1;
+    public static final int PLAYER_TYPE_UNKNOWN = -1;
     /**
      * @hide
      * Player backed by a java android.media.AudioTrack player
      */
     @SystemApi
-    public final static int PLAYER_TYPE_JAM_AUDIOTRACK = 1;
+    public static final int PLAYER_TYPE_JAM_AUDIOTRACK = 1;
     /**
      * @hide
      * Player backed by a java android.media.MediaPlayer player
      */
     @SystemApi
-    public final static int PLAYER_TYPE_JAM_MEDIAPLAYER = 2;
+    public static final int PLAYER_TYPE_JAM_MEDIAPLAYER = 2;
     /**
      * @hide
      * Player backed by a java android.media.SoundPool player
      */
     @SystemApi
-    public final static int PLAYER_TYPE_JAM_SOUNDPOOL = 3;
+    public static final int PLAYER_TYPE_JAM_SOUNDPOOL = 3;
     /**
      * @hide
      * Player backed by a C OpenSL ES AudioPlayer player with a BufferQueue source
      */
     @SystemApi
-    public final static int PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11;
+    public static final int PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11;
     /**
      * @hide
      * Player backed by a C OpenSL ES AudioPlayer player with a URI or FD source
      */
     @SystemApi
-    public final static int PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12;
+    public static final int PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12;
+
+    /**
+     * @hide
+     * Player backed an AAudio player.
+     * Note this type is not in System API so it will not be returned in public API calls
+     */
+    // TODO unhide for SystemApi, update getPlayerType()
+    public static final int PLAYER_TYPE_AAUDIO = 13;
+
+    /**
+     * @hide
+     * Player backed a hardware source, whose state is visible in the Android audio policy manager.
+     * Note this type is not in System API so it will not be returned in public API calls
+     */
+    // TODO unhide for SystemApi, update getPlayerType()
+    public static final int PLAYER_TYPE_HW_SOURCE = 14;
+
+    /**
+     * @hide
+     * Player is a proxy for an audio player whose audio and state doesn't go through the Android
+     * audio framework.
+     * Note this type is not in System API so it will not be returned in public API calls
+     */
+    // TODO unhide for SystemApi, update getPlayerType()
+    public static final int PLAYER_TYPE_EXTERNAL_PROXY = 15;
 
     /** @hide */
     @IntDef({
@@ -251,11 +276,20 @@
      * {@link #PLAYER_TYPE_JAM_AUDIOTRACK}, {@link #PLAYER_TYPE_JAM_MEDIAPLAYER},
      * {@link #PLAYER_TYPE_JAM_SOUNDPOOL}, {@link #PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE},
      * {@link #PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD}, or {@link #PLAYER_TYPE_UNKNOWN}.
+     * <br>Note that player types not exposed in the system API will be represented as
+     * {@link #PLAYER_TYPE_UNKNOWN}.
      * @return the type of the player.
      */
     @SystemApi
     public @PlayerType int getPlayerType() {
-        return mPlayerType;
+        switch (mPlayerType) {
+            case PLAYER_TYPE_AAUDIO:
+            case PLAYER_TYPE_HW_SOURCE:
+            case PLAYER_TYPE_EXTERNAL_PROXY:
+                return PLAYER_TYPE_UNKNOWN;
+            default:
+                return mPlayerType;
+        }
     }
 
     /**
@@ -442,7 +476,7 @@
 
     //=====================================================================
     // Inner class for corresponding IPlayer and its death monitoring
-    final static class IPlayerShell implements IBinder.DeathRecipient {
+    static final class IPlayerShell implements IBinder.DeathRecipient {
 
         final AudioPlaybackConfiguration mMonitor; // never null
         private IPlayer mIPlayer;
@@ -494,6 +528,9 @@
                 return "OpenSL ES AudioPlayer (Buffer Queue)";
             case PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD:
                 return "OpenSL ES AudioPlayer (URI/FD)";
+            case PLAYER_TYPE_AAUDIO: return "AAudio";
+            case PLAYER_TYPE_HW_SOURCE: return "hardware source";
+            case PLAYER_TYPE_EXTERNAL_PROXY: return "external proxy";
             default:
                 return "unknown player type - FIXME";
         }
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 1ddbcad..348b02a 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -966,17 +966,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 79b02a5..c4e134b 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -531,7 +531,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/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 6604b6c..4245b11 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -249,7 +249,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..854696e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -716,9 +716,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/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 8596b57..84524a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -24,6 +24,7 @@
 public interface QSHost {
     void warn(String message, Throwable t);
     void collapsePanels();
+    void forceCollapsePanels();
     void openPanels();
     Context getContext();
     Collection<QSTile> getTiles();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 9330541..02937d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -143,6 +143,11 @@
     }
 
     @Override
+    public void forceCollapsePanels() {
+        mStatusBar.postAnimateForceCollapsePanels();
+    }
+
+    @Override
     public void openPanels() {
         mStatusBar.postAnimateOpenPanels();
     }
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/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index d2f3bb6..943a176 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -204,7 +204,7 @@
         if (customTile != null) {
             verifyCaller(customTile);
             customTile.onDialogShown();
-            mHost.collapsePanels();
+            mHost.forceCollapsePanels();
             mServices.get(customTile).setShowingDialog(true);
         }
     }
@@ -224,7 +224,7 @@
         CustomTile customTile = getTileForToken(token);
         if (customTile != null) {
             verifyCaller(customTile);
-            mHost.collapsePanels();
+            mHost.forceCollapsePanels();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 012accd..9e2ec57 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 3542500..a81b140 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;
@@ -1121,6 +1122,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/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/SignalDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
index a9eb20b..8cd8791 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
@@ -30,6 +30,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
+import android.util.LayoutDirection;
 import android.util.Log;
 
 import com.android.settingslib.R;
@@ -192,6 +193,13 @@
 
     @Override
     public void draw(@NonNull Canvas canvas) {
+        boolean isRtl = getLayoutDirection() == LayoutDirection.RTL;
+        if (isRtl) {
+            canvas.save();
+            // Mirror the drawable
+            canvas.translate(canvas.getWidth(), 0);
+            canvas.scale(-1.0f, 1.0f);
+        }
         mFullPath.reset();
         mFullPath.setFillType(FillType.WINDING);
         float width = getBounds().width();
@@ -250,6 +258,9 @@
             }
             canvas.drawPath(mXPath, mForegroundPaint);
         }
+        if (isRtl) {
+            canvas.restore();
+        }
     }
 
     private void drawDot(Path fullPath, Path foregroundPath, float x, float y, float dotSize,
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 d2ab823..07ab6876 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -759,6 +759,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();
@@ -1614,9 +1615,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) {
@@ -2878,6 +2884,15 @@
         mHandler.post(mAnimateCollapsePanels);
     }
 
+    public void postAnimateForceCollapsePanels() {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
+            }
+        });
+    }
+
     public void postAnimateOpenPanels() {
         mHandler.sendEmptyMessage(MSG_OPEN_SETTINGS_PANEL);
     }
@@ -5178,6 +5193,7 @@
     protected KeyguardManager mKeyguardManager;
     private LockPatternUtils mLockPatternUtils;
     private DeviceProvisionedController mDeviceProvisionedController;
+    protected SystemServicesProxy mSystemServicesProxy;
 
     // UI-specific methods
 
@@ -6235,7 +6251,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);
 
@@ -6799,6 +6818,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;
         }
 
@@ -6807,8 +6827,7 @@
             return false;
         }
 
-        boolean inUse = mPowerManager.isScreenOn()
-                && !SystemServicesProxy.getInstance(mContext).isDreaming();
+        boolean inUse = mPowerManager.isScreenOn() && !mSystemServicesProxy.isDreaming();
 
         if (!inUse && !isDozing()) {
             if (DEBUG) {
@@ -6851,6 +6870,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 eaf8925..78e0028 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
@@ -14,9 +14,6 @@
 
 package com.android.systemui.statusbar.policy;
 
-import com.android.systemui.Dependency;
-import com.android.systemui.plugins.Plugin;
-
 import java.util.Map;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
@@ -32,6 +29,11 @@
     interface Extension<T> {
         T get();
         void destroy();
+        /**
+         * 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 fefbaa3..cfc1d56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -126,6 +126,12 @@
             }
         }
 
+        @Override
+        public T reload() {
+            notifyChanged();
+            return get();
+        }
+
         private void notifyChanged() {
             for (int i = 0; i < mProducers.size(); i++) {
                 final T item = mProducers.get(i).get();
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..cc7943b 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 0dbd1d6..ff06b5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -300,7 +300,7 @@
         mNotificationHeaderWrapper.notifyContentUpdated(mContainingNotification);
         recreateLowPriorityHeader(builder);
         recreateAmbientHeader(builder);
-        resetHeaderVisibilityIfNeeded(mNotificationHeader, calculateDesiredHeader());
+        updateHeaderVisibility(false /* animate */);
         updateChildrenHeaderAppearance();
     }
 
@@ -833,6 +833,11 @@
         return mNotificationHeaderLowPriority;
     }
 
+    @VisibleForTesting
+    public ViewGroup getCurrentHeaderView() {
+        return mCurrentHeader;
+    }
+
     public void notifyShowAmbientChanged() {
         updateHeaderVisibility(false);
     }
@@ -869,7 +874,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);
             }
         }
@@ -878,7 +888,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 51fcdbb..ecdea4f 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;
@@ -294,11 +293,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;
@@ -311,6 +305,7 @@
         setSessionExitCondition(copy(mExitCondition));
         updateWidgets();
         setRequestingConditions(!mHidden);
+        ensureSelection();
     }
 
     private void onDetach() {
@@ -366,9 +361,6 @@
         if (expanded == mExpanded) return;
         if (DEBUG) Log.d(mTag, "setExpanded " + expanded);
         mExpanded = expanded;
-        if (mExpanded) {
-            ensureSelection();
-        }
         updateWidgets();
         fireExpanded();
     }
@@ -464,7 +456,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) {
@@ -491,6 +484,7 @@
             final ConditionTag tag = getConditionTagAt(i);
             if (tag != null && sameConditionId(tag.condition, mExitCondition)) {
                 bind(exitCondition, mZenRadioGroupContent.getChildAt(i), i);
+                tag.rb.setChecked(true);
                 return;
             }
         }
@@ -498,6 +492,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..8124bf3
--- /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 e6c8815..2dd96b6 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
@@ -60,4 +60,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 b9d188a..00846dc 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
@@ -97,5 +97,10 @@
         public void destroy() {
             mTracker.getLeakInfo(mAllocation).clearAllocations();
         }
+
+        @Override
+        public T reload() {
+            return null;
+        }
     }
 }
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/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/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 9e054c3..3460419 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1727,7 +1727,7 @@
                             record.mReceiver.mIdentity.mUid,
                             record.mReceiver.mIdentity.mPackageName,
                             record.mReceiver.mAllowedResolutionLevel)) {
-                        LocationRequest locationRequest = record.mRequest;
+                        LocationRequest locationRequest = record.mRealRequest;
                         long interval = locationRequest.getInterval();
 
                         if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
@@ -1740,6 +1740,7 @@
                             }
                         }
 
+                        record.mRequest = locationRequest;
                         providerRequest.locationRequests.add(locationRequest);
                         if (interval < providerRequest.interval) {
                             providerRequest.reportLocation = true;
@@ -1832,7 +1833,8 @@
 
     private class UpdateRecord {
         final String mProvider;
-        final LocationRequest mRequest;
+        final LocationRequest mRealRequest;  // original request from client
+        LocationRequest mRequest;  // possibly throttled version of the request
         final Receiver mReceiver;
         boolean mIsForegroundUid;
         Location mLastFixBroadcast;
@@ -1843,6 +1845,7 @@
          */
         UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
             mProvider = provider;
+            mRealRequest = request;
             mRequest = request;
             mReceiver = receiver;
             mIsForegroundUid = isImportanceForeground(
@@ -1892,7 +1895,7 @@
         public String toString() {
             return "UpdateRecord[" + mProvider + " " + mReceiver.mIdentity.mPackageName
                     + "(" + mReceiver.mIdentity.mUid + (mIsForegroundUid ? " foreground" : " background")
-                    + ")" + " " + mRequest + "]";
+                    + ")" + " " + mRealRequest + "]";
         }
     }
 
@@ -2533,7 +2536,7 @@
         }
 
         // Check whether sufficient time has passed
-        long minTime = record.mRequest.getFastestInterval();
+        long minTime = record.mRealRequest.getFastestInterval();
         long delta = (loc.getElapsedRealtimeNanos() - lastLoc.getElapsedRealtimeNanos())
                 / NANOS_PER_MILLI;
         if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
@@ -2541,7 +2544,7 @@
         }
 
         // Check whether sufficient distance has been traveled
-        double minDistance = record.mRequest.getSmallestDisplacement();
+        double minDistance = record.mRealRequest.getSmallestDisplacement();
         if (minDistance > 0.0) {
             if (loc.distanceTo(lastLoc) <= minDistance) {
                 return false;
@@ -2549,12 +2552,12 @@
         }
 
         // Check whether sufficient number of udpates is left
-        if (record.mRequest.getNumUpdates() <= 0) {
+        if (record.mRealRequest.getNumUpdates() <= 0) {
             return false;
         }
 
         // Check whether the expiry date has passed
-        return record.mRequest.getExpireAt() >= now;
+        return record.mRealRequest.getExpireAt() >= now;
     }
 
     private void handleLocationChangedLocked(Location location, boolean passive) {
@@ -2672,7 +2675,7 @@
                         Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
                         receiverDead = true;
                     }
-                    r.mRequest.decrementNumUpdates();
+                    r.mRealRequest.decrementNumUpdates();
                 }
             }
 
@@ -2688,7 +2691,7 @@
             }
 
             // track expired records
-            if (r.mRequest.getNumUpdates() <= 0 || r.mRequest.getExpireAt() < now) {
+            if (r.mRealRequest.getNumUpdates() <= 0 || r.mRealRequest.getExpireAt() < now) {
                 if (deadUpdateRecords == null) {
                     deadUpdateRecords = new ArrayList<>();
                 }
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 fbab26a..68c92a8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8447,8 +8447,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;
@@ -13545,7 +13545,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 93e1ab4..9db957c 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 e180aef..e828d38 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2989,8 +2989,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 7eec945..e81e6d7 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);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4668156..3667e16 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
@@ -3224,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();
         }
     }
@@ -3659,14 +3660,6 @@
                             " intercept=" + record.isIntercepted()
             );
 
-        final int currentUser;
-        final long token = Binder.clearCallingIdentity();
-        try {
-            currentUser = ActivityManager.getCurrentUser();
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-
         // If we're not supposed to beep, vibrate, etc. then don't.
         final String disableEffects = disableNotificationEffects(record);
         if (disableEffects != null) {
@@ -3676,50 +3669,53 @@
         // Remember if this notification already owns the notification channels.
         boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
         boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
-
         // These are set inside the conditional if the notification is allowed to make noise.
         boolean hasValidVibrate = false;
         boolean hasValidSound = false;
-        if (disableEffects == null
-                && (record.getUserId() == UserHandle.USER_ALL ||
-                    record.getUserId() == currentUser ||
-                    mUserProfiles.isCurrentProfile(record.getUserId()))
-                && canInterrupt
-                && mSystemReady
-                && mAudioManager != null) {
-            if (DBG) Slog.v(TAG, "Interrupting!");
 
-            Uri soundUri = record.getSound();
-            hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
-            long[] vibration = record.getVibration();
-            // Demote sound to vibration if vibration missing & phone in vibration mode.
-            if (vibration == null
-                    && hasValidSound
-                    && (mAudioManager.getRingerModeInternal()
-                            == AudioManager.RINGER_MODE_VIBRATE)) {
-                vibration = mFallbackVibrationPattern;
-            }
-            hasValidVibrate = vibration != null;
-
-            if (!shouldMuteNotificationLocked(record)) {
+        if (isNotificationForCurrentUser(record)) {
+            // If the notification will appear in the status bar, it should send an accessibility
+            // event
+            if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
                 sendAccessibilityEvent(notification, record.sbn.getPackageName());
+            }
 
-                if (hasValidSound) {
-                    mSoundNotificationKey = key;
-                    if (mInCall) {
-                        playInCallNotification();
-                        beep = true;
-                    } else {
-                        beep = playSound(record, soundUri);
-                    }
+            if (disableEffects == null
+                    && canInterrupt
+                    && mSystemReady
+                    && mAudioManager != null) {
+                if (DBG) Slog.v(TAG, "Interrupting!");
+                Uri soundUri = record.getSound();
+                hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
+                long[] vibration = record.getVibration();
+                // Demote sound to vibration if vibration missing & phone in vibration mode.
+                if (vibration == null
+                        && hasValidSound
+                        && (mAudioManager.getRingerModeInternal()
+                        == AudioManager.RINGER_MODE_VIBRATE)) {
+                    vibration = mFallbackVibrationPattern;
                 }
+                hasValidVibrate = vibration != null;
 
-                final boolean ringerModeSilent =
-                        mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT;
-                if (!mInCall && hasValidVibrate && !ringerModeSilent) {
-                    mVibrateNotificationKey = key;
+                if (!shouldMuteNotificationLocked(record)) {
+                    if (hasValidSound) {
+                        mSoundNotificationKey = key;
+                        if (mInCall) {
+                            playInCallNotification();
+                            beep = true;
+                        } else {
+                            beep = playSound(record, soundUri);
+                        }
+                    }
 
-                    buzz = playVibration(record, vibration);
+                    final boolean ringerModeSilent =
+                            mAudioManager.getRingerModeInternal()
+                                    == AudioManager.RINGER_MODE_SILENT;
+                    if (!mInCall && hasValidVibrate && !ringerModeSilent) {
+                        mVibrateNotificationKey = key;
+
+                        buzz = playVibration(record, vibration);
+                    }
                 }
             }
         }
@@ -3763,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;
     }
@@ -3819,6 +3809,19 @@
         }
     }
 
+    private boolean isNotificationForCurrentUser(NotificationRecord record) {
+        final int currentUser;
+        final long token = Binder.clearCallingIdentity();
+        try {
+            currentUser = ActivityManager.getCurrentUser();
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        return (record.getUserId() == UserHandle.USER_ALL ||
+                record.getUserId() == currentUser ||
+                mUserProfiles.isCurrentProfile(record.getUserId()));
+    }
+
     private void playInCallNotification() {
         new Thread() {
             @Override
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 e6e4617..c95b5c5 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -395,10 +395,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 782325a..99eda86 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -875,9 +875,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) {
@@ -1312,6 +1312,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
@@ -4155,13 +4158,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);
@@ -4178,13 +4181,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);
@@ -4209,10 +4212,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)
@@ -4230,29 +4237,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;
@@ -4261,6 +4291,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.
      */
@@ -10650,8 +10743,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);
@@ -15494,7 +15586,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) {
@@ -18488,7 +18580,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;
 
@@ -18531,7 +18623,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(
@@ -24273,9 +24365,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 908e517..e3bc919 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 c8ade2d..3b5db29 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;
@@ -1940,7 +1941,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;
@@ -2728,9 +2730,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 fbc9e56..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,244 +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();
-        //create StatFs object
-        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() {
@@ -408,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),
@@ -445,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());
         }
     };
 
@@ -494,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;
@@ -518,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;
@@ -529,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;
@@ -540,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;
@@ -568,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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1d44a205..6f53099 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -185,6 +185,8 @@
             "com.google.android.clockwork.ThermalObserver";
     private static final String WEAR_CONNECTIVITY_SERVICE_CLASS =
             "com.google.android.clockwork.connectivity.WearConnectivityService";
+    private static final String WEAR_DISPLAY_SERVICE_CLASS =
+            "com.google.android.clockwork.display.WearDisplayService";
     private static final String WEAR_TIME_SERVICE_CLASS =
             "com.google.android.clockwork.time.WearTimeService";
     private static final String ACCOUNT_SERVICE_CLASS =
@@ -1499,6 +1501,7 @@
 
             if (!disableNonCoreServices) {
                 traceBeginAndSlog("StartWearTimeService");
+                mSystemServiceManager.startService(WEAR_DISPLAY_SERVICE_CLASS);
                 mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS);
                 traceEnd();
             }
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/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/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index b460258..bf18949 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -99,6 +99,7 @@
         "link/PrivateAttributeMover.cpp",
         "link/ReferenceLinker.cpp",
         "link/TableMerger.cpp",
+        "link/XmlCompatVersioner.cpp",
         "link/XmlNamespaceRemover.cpp",
         "link/XmlReferenceLinker.cpp",
         "optimize/ResourceDeduper.cpp",
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index 56e2e95..e2456c7 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -37,6 +37,7 @@
                               const ResourceName& target_style);
   static void DumpHex(const void* data, size_t len);
   static void DumpXml(xml::XmlResource* doc);
+  static std::string ToString(xml::XmlResource* doc);
 };
 
 }  // namespace aapt
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/Main.cpp b/tools/aapt2/Main.cpp
index e45d142..1d2e3a4 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -27,7 +27,7 @@
 static const char* sMajorVersion = "2";
 
 // Update minor version whenever a feature or flag is added.
-static const char* sMinorVersion = "15";
+static const char* sMinorVersion = "16";
 
 int PrintVersion() {
   std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 493f238..0a74c1a 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -388,13 +388,20 @@
 struct hash<aapt::ResourceName> {
   size_t operator()(const aapt::ResourceName& name) const {
     android::hash_t h = 0;
-    h = android::JenkinsHashMix(h, hash<string>()(name.package));
+    h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.package)));
     h = android::JenkinsHashMix(h, static_cast<uint32_t>(name.type));
-    h = android::JenkinsHashMix(h, hash<string>()(name.entry));
+    h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.entry)));
     return static_cast<size_t>(h);
   }
 };
 
+template <>
+struct hash<aapt::ResourceId> {
+  size_t operator()(const aapt::ResourceId& id) const {
+    return id.id;
+  }
+};
+
 }  // namespace std
 
 #endif  // AAPT_RESOURCE_H
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 0d1850f..57ae270 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/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 0cb8c67..34bd2b4 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -333,6 +333,12 @@
   }
 }
 
+Attribute::Attribute()
+    : type_mask(0u),
+      min_int(std::numeric_limits<int32_t>::min()),
+      max_int(std::numeric_limits<int32_t>::max()) {
+}
+
 Attribute::Attribute(bool w, uint32_t t)
     : type_mask(t),
       min_int(std::numeric_limits<int32_t>::min()),
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index c71c738..06f949f 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -18,6 +18,7 @@
 #define AAPT_RESOURCE_VALUES_H
 
 #include <array>
+#include <limits>
 #include <ostream>
 #include <vector>
 
@@ -251,6 +252,7 @@
   int32_t max_int;
   std::vector<Symbol> symbols;
 
+  Attribute();
   explicit Attribute(bool w, uint32_t t = 0u);
 
   bool Equals(const Value* value) const override;
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index e806714..041cb4f 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -26,9 +26,9 @@
 namespace aapt {
 
 static const char* sDevelopmentSdkCodeName = "O";
-static int sDevelopmentSdkLevel = 26;
+static ApiVersion sDevelopmentSdkLevel = 26;
 
-static const std::vector<std::pair<uint16_t, size_t>> sAttrIdMap = {
+static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
     {0x021c, 1},
     {0x021d, 2},
     {0x0269, SDK_CUPCAKE},
@@ -48,26 +48,29 @@
     {0x03f1, SDK_KITKAT},
     {0x03f6, SDK_KITKAT_WATCH},
     {0x04ce, SDK_LOLLIPOP},
+    {0x04d8, SDK_LOLLIPOP_MR1},
+    {0x04f1, SDK_MARSHMALLOW},
+    {0x0527, SDK_NOUGAT},
+    {0x0530, SDK_NOUGAT_MR1},
+    {0x0568, SDK_O},
 };
 
-static bool less_entry_id(const std::pair<uint16_t, size_t>& p,
-                        uint16_t entryId) {
+static bool less_entry_id(const std::pair<uint16_t, ApiVersion>& p, uint16_t entryId) {
   return p.first < entryId;
 }
 
-size_t FindAttributeSdkLevel(const ResourceId& id) {
-  if (id.package_id() != 0x01 && id.type_id() != 0x01) {
+ApiVersion FindAttributeSdkLevel(const ResourceId& id) {
+  if (id.package_id() != 0x01 || id.type_id() != 0x01) {
     return 0;
   }
-  auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(),
-                               id.entry_id(), less_entry_id);
+  auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(), id.entry_id(), less_entry_id);
   if (iter == sAttrIdMap.end()) {
     return SDK_LOLLIPOP_MR1;
   }
   return iter->second;
 }
 
-static const std::unordered_map<std::string, size_t> sAttrMap = {
+static const std::unordered_map<std::string, ApiVersion> sAttrMap = {
     {"marqueeRepeatLimit", 2},
     {"windowNoDisplay", 3},
     {"backgroundDimEnabled", 3},
@@ -729,7 +732,7 @@
     {"windowActivityTransitions", 21},
     {"colorEdgeEffect", 21}};
 
-size_t FindAttributeSdkLevel(const ResourceName& name) {
+ApiVersion FindAttributeSdkLevel(const ResourceName& name) {
   if (name.package != "android" && name.type != ResourceType::kAttr) {
     return 0;
   }
@@ -741,9 +744,8 @@
   return SDK_LOLLIPOP_MR1;
 }
 
-std::pair<StringPiece, int> GetDevelopmentSdkCodeNameAndVersion() {
-  return std::make_pair(StringPiece(sDevelopmentSdkCodeName),
-                        sDevelopmentSdkLevel);
+std::pair<StringPiece, ApiVersion> GetDevelopmentSdkCodeNameAndVersion() {
+  return std::make_pair(StringPiece(sDevelopmentSdkCodeName), sDevelopmentSdkLevel);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index c2ee252..e3745e8 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -25,7 +25,9 @@
 
 namespace aapt {
 
-enum : int {
+using ApiVersion = int;
+
+enum : ApiVersion {
   SDK_CUPCAKE = 3,
   SDK_DONUT = 4,
   SDK_ECLAIR = 5,
@@ -49,12 +51,12 @@
   SDK_MARSHMALLOW = 23,
   SDK_NOUGAT = 24,
   SDK_NOUGAT_MR1 = 25,
-  SDK_O = 26,  // STOPSHIP Replace with real version
+  SDK_O = 26,
 };
 
-size_t FindAttributeSdkLevel(const ResourceId& id);
-size_t FindAttributeSdkLevel(const ResourceName& name);
-std::pair<android::StringPiece, int> GetDevelopmentSdkCodeNameAndVersion();
+ApiVersion FindAttributeSdkLevel(const ResourceId& id);
+ApiVersion FindAttributeSdkLevel(const ResourceName& name);
+std::pair<android::StringPiece, ApiVersion> GetDevelopmentSdkCodeNameAndVersion();
 
 }  // namespace aapt
 
diff --git a/tools/aapt2/SdkConstants_test.cpp b/tools/aapt2/SdkConstants_test.cpp
index 716d922..61f4d71 100644
--- a/tools/aapt2/SdkConstants_test.cpp
+++ b/tools/aapt2/SdkConstants_test.cpp
@@ -21,18 +21,11 @@
 namespace aapt {
 
 TEST(SdkConstantsTest, FirstAttributeIsSdk1) {
-  EXPECT_EQ(1u, FindAttributeSdkLevel(ResourceId(0x01010000)));
+  EXPECT_EQ(1, FindAttributeSdkLevel(ResourceId(0x01010000)));
 }
 
-TEST(SdkConstantsTest, AllAttributesAfterLollipopAreLollipopMR1) {
-  EXPECT_EQ(SDK_LOLLIPOP, FindAttributeSdkLevel(ResourceId(0x010103f7)));
-  EXPECT_EQ(SDK_LOLLIPOP, FindAttributeSdkLevel(ResourceId(0x010104ce)));
-
-  EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x010104cf)));
-  EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x010104d8)));
-
-  EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x010104d9)));
-  EXPECT_EQ(SDK_LOLLIPOP_MR1, FindAttributeSdkLevel(ResourceId(0x0101ffff)));
+TEST(SdkConstantsTest, NonFrameworkAttributeIsSdk0) {
+  EXPECT_EQ(0, FindAttributeSdkLevel(ResourceId(0x7f010345)));
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 5adf04a..e09a3979 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -478,7 +478,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)
                                        << android::base::SystemErrorCodeToString(errno));
       return false;
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 8accfa8..dd4b2ba 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -50,6 +50,7 @@
 #include "link/ManifestFixer.h"
 #include "link/ReferenceLinker.h"
 #include "link/TableMerger.h"
+#include "link/XmlCompatVersioner.h"
 #include "optimize/ResourceDeduper.h"
 #include "optimize/VersionCollapser.h"
 #include "process/IResourceTableConsumer.h"
@@ -247,25 +248,20 @@
   IAaptContext* context_;
 };
 
-static bool FlattenXml(xml::XmlResource* xml_res, const StringPiece& path,
-                       Maybe<size_t> max_sdk_level, bool keep_raw_values, IArchiveWriter* writer,
-                       IAaptContext* context) {
+static bool FlattenXml(IAaptContext* context, xml::XmlResource* xml_res, const StringPiece& path,
+                       bool keep_raw_values, IArchiveWriter* writer) {
   BigBuffer buffer(1024);
   XmlFlattenerOptions options = {};
   options.keep_raw_values = keep_raw_values;
-  options.max_sdk_level = max_sdk_level;
   XmlFlattener flattener(&buffer, options);
   if (!flattener.Consume(context, xml_res)) {
     return false;
   }
 
   if (context->IsVerbose()) {
-    DiagMessage msg;
-    msg << "writing " << path << " to archive";
-    if (max_sdk_level) {
-      msg << " maxSdkLevel=" << max_sdk_level.value() << " keepRawValues=" << keep_raw_values;
-    }
-    context->GetDiagnostics()->Note(msg);
+    context->GetDiagnostics()->Note(DiagMessage(path) << "writing to archive (keep_raw_values="
+                                                      << (keep_raw_values ? "true" : "false")
+                                                      << ")");
   }
 
   io::BigBufferInputStream input_stream(&buffer);
@@ -311,12 +307,33 @@
   std::unordered_set<std::string> extensions_to_not_compress;
 };
 
+// A sampling of public framework resource IDs.
+struct R {
+  struct attr {
+    enum : uint32_t {
+      paddingLeft = 0x010100d6u,
+      paddingRight = 0x010100d8u,
+      paddingHorizontal = 0x0101053du,
+
+      paddingTop = 0x010100d7u,
+      paddingBottom = 0x010100d9u,
+      paddingVertical = 0x0101053eu,
+
+      layout_marginLeft = 0x010100f7u,
+      layout_marginRight = 0x010100f9u,
+      layout_marginHorizontal = 0x0101053bu,
+
+      layout_marginTop = 0x010100f8u,
+      layout_marginBottom = 0x010100fau,
+      layout_marginVertical = 0x0101053cu,
+    };
+  };
+};
+
 class ResourceFileFlattener {
  public:
   ResourceFileFlattener(const ResourceFileFlattenerOptions& options, IAaptContext* context,
-                        proguard::KeepSet* keep_set)
-      : options_(options), context_(context), keep_set_(keep_set) {
-  }
+                        proguard::KeepSet* keep_set);
 
   bool Flatten(ResourceTable* table, IArchiveWriter* archive_writer);
 
@@ -325,7 +342,7 @@
     ConfigDescription config;
 
     // The entry this file came from.
-    const ResourceEntry* entry;
+    ResourceEntry* entry;
 
     // The file to copy as-is.
     io::IFile* file_to_copy;
@@ -335,19 +352,72 @@
 
     // The destination to write this file to.
     std::string dst_path;
-    bool skip_version = false;
   };
 
   uint32_t GetCompressionFlags(const StringPiece& str);
 
-  bool LinkAndVersionXmlFile(ResourceTable* table, FileOperation* file_op,
-                             std::queue<FileOperation>* out_file_op_queue);
+  std::vector<std::unique_ptr<xml::XmlResource>> LinkAndVersionXmlFile(ResourceTable* table,
+                                                                       FileOperation* file_op);
 
   ResourceFileFlattenerOptions options_;
   IAaptContext* context_;
   proguard::KeepSet* keep_set_;
+  XmlCompatVersioner::Rules rules_;
 };
 
+ResourceFileFlattener::ResourceFileFlattener(const ResourceFileFlattenerOptions& options,
+                                             IAaptContext* context, proguard::KeepSet* keep_set)
+    : options_(options), context_(context), keep_set_(keep_set) {
+  SymbolTable* symm = context_->GetExternalSymbols();
+
+  // Build up the rules for degrading newer attributes to older ones.
+  // NOTE(adamlesinski): These rules are hardcoded right now, but they should be
+  // generated from the attribute definitions themselves (b/62028956).
+  if (const SymbolTable::Symbol* s = symm->FindById(R::attr::paddingHorizontal)) {
+    std::vector<ReplacementAttr> replacements{
+        {"paddingLeft", R::attr::paddingLeft,
+         Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+        {"paddingRight", R::attr::paddingRight,
+         Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+    };
+    rules_[R::attr::paddingHorizontal] =
+        util::make_unique<DegradeToManyRule>(std::move(replacements));
+  }
+
+  if (const SymbolTable::Symbol* s = symm->FindById(R::attr::paddingVertical)) {
+    std::vector<ReplacementAttr> replacements{
+        {"paddingTop", R::attr::paddingTop,
+         Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+        {"paddingBottom", R::attr::paddingBottom,
+         Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+    };
+    rules_[R::attr::paddingVertical] =
+        util::make_unique<DegradeToManyRule>(std::move(replacements));
+  }
+
+  if (const SymbolTable::Symbol* s = symm->FindById(R::attr::layout_marginHorizontal)) {
+    std::vector<ReplacementAttr> replacements{
+        {"layout_marginLeft", R::attr::layout_marginLeft,
+         Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+        {"layout_marginRight", R::attr::layout_marginRight,
+         Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+    };
+    rules_[R::attr::layout_marginHorizontal] =
+        util::make_unique<DegradeToManyRule>(std::move(replacements));
+  }
+
+  if (const SymbolTable::Symbol* s = symm->FindById(R::attr::layout_marginVertical)) {
+    std::vector<ReplacementAttr> replacements{
+        {"layout_marginTop", R::attr::layout_marginTop,
+         Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+        {"layout_marginBottom", R::attr::layout_marginBottom,
+         Attribute(false, android::ResTable_map::TYPE_DIMENSION)},
+    };
+    rules_[R::attr::layout_marginVertical] =
+        util::make_unique<DegradeToManyRule>(std::move(replacements));
+  }
+}
+
 uint32_t ResourceFileFlattener::GetCompressionFlags(const StringPiece& str) {
   if (options_.do_not_compress_anything) {
     return 0;
@@ -369,8 +439,19 @@
          name == "transitionManager";
 }
 
-bool ResourceFileFlattener::LinkAndVersionXmlFile(ResourceTable* table, FileOperation* file_op,
-                                                  std::queue<FileOperation>* out_file_op_queue) {
+static bool IsVectorElement(const std::string& name) {
+  return name == "vector" || name == "animated-vector";
+}
+
+template <typename T>
+std::vector<T> make_singleton_vec(T&& val) {
+  std::vector<T> vec;
+  vec.emplace_back(std::forward<T>(val));
+  return vec;
+}
+
+std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVersionXmlFile(
+    ResourceTable* table, FileOperation* file_op) {
   xml::XmlResource* doc = file_op->xml_to_flatten.get();
   const Source& src = doc->file.source;
 
@@ -380,107 +461,60 @@
 
   XmlReferenceLinker xml_linker;
   if (!xml_linker.Consume(context_, doc)) {
-    return false;
+    return {};
   }
 
   if (options_.update_proguard_spec && !proguard::CollectProguardRules(src, doc, keep_set_)) {
-    return false;
+    return {};
   }
 
   if (options_.no_xml_namespaces) {
     XmlNamespaceRemover namespace_remover;
     if (!namespace_remover.Consume(context_, doc)) {
-      return false;
+      return {};
     }
   }
 
-  if (!options_.no_auto_version) {
-    if (options_.no_version_vectors) {
-      // Skip this if it is a vector or animated-vector.
-      xml::Element* el = xml::FindRootElement(doc);
-      if (el && el->namespace_uri.empty()) {
-        if (el->name == "vector" || el->name == "animated-vector") {
-          // We are NOT going to version this file.
-          file_op->skip_version = true;
-          return true;
-        }
-      }
-    }
-    if (options_.no_version_transitions) {
-      // Skip this if it is a transition resource.
-      xml::Element* el = xml::FindRootElement(doc);
-      if (el && el->namespace_uri.empty()) {
-        if (IsTransitionElement(el->name)) {
-          // We are NOT going to version this file.
-          file_op->skip_version = true;
-          return true;
-        }
-      }
-    }
+  if (options_.no_auto_version) {
+    return make_singleton_vec(std::move(file_op->xml_to_flatten));
+  }
 
-    const ConfigDescription& config = file_op->config;
-
-    // Find the first SDK level used that is higher than this defined config and
-    // not superseded by a lower or equal SDK level resource.
-    const int min_sdk_version = context_->GetMinSdkVersion();
-    for (int sdk_level : xml_linker.sdk_levels()) {
-      if (sdk_level > min_sdk_version && sdk_level > config.sdkVersion) {
-        if (!ShouldGenerateVersionedResource(file_op->entry, config, sdk_level)) {
-          // If we shouldn't generate a versioned resource, stop checking.
-          break;
-        }
-
-        ResourceFile versioned_file_desc = doc->file;
-        versioned_file_desc.config.sdkVersion = (uint16_t)sdk_level;
-
-        FileOperation new_file_op;
-        new_file_op.xml_to_flatten = util::make_unique<xml::XmlResource>(
-            versioned_file_desc, StringPool{}, doc->root->Clone());
-        new_file_op.config = versioned_file_desc.config;
-        new_file_op.entry = file_op->entry;
-        new_file_op.dst_path =
-            ResourceUtils::BuildResourceFileName(versioned_file_desc, context_->GetNameMangler());
-
-        if (context_->IsVerbose()) {
-          context_->GetDiagnostics()->Note(DiagMessage(versioned_file_desc.source)
-                                           << "auto-versioning resource from config '" << config
-                                           << "' -> '" << versioned_file_desc.config << "'");
-        }
-
-        bool added = table->AddFileReferenceAllowMangled(
-            versioned_file_desc.name, versioned_file_desc.config, versioned_file_desc.source,
-            new_file_op.dst_path, nullptr, context_->GetDiagnostics());
-        if (!added) {
-          return false;
-        }
-
-        out_file_op_queue->push(std::move(new_file_op));
-        break;
+  if (options_.no_version_vectors || options_.no_version_transitions) {
+    // Skip this if it is a vector or animated-vector.
+    xml::Element* el = xml::FindRootElement(doc);
+    if (el && el->namespace_uri.empty()) {
+      if ((options_.no_version_vectors && IsVectorElement(el->name)) ||
+          (options_.no_version_transitions && IsTransitionElement(el->name))) {
+        return make_singleton_vec(std::move(file_op->xml_to_flatten));
       }
     }
   }
-  return true;
+
+  const ConfigDescription& config = file_op->config;
+  ResourceEntry* entry = file_op->entry;
+
+  XmlCompatVersioner xml_compat_versioner(&rules_);
+  const util::Range<ApiVersion> api_range{config.sdkVersion,
+                                          FindNextApiVersionForConfig(entry, config)};
+  return xml_compat_versioner.Process(context_, doc, api_range);
 }
 
-/**
- * Do not insert or remove any resources while executing in this function. It
- * will
- * corrupt the iteration order.
- */
 bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archive_writer) {
   bool error = false;
   std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files;
 
   for (auto& pkg : table->packages) {
     for (auto& type : pkg->types) {
-      // Sort by config and name, so that we get better locality in the zip
-      // file.
+      // Sort by config and name, so that we get better locality in the zip file.
       config_sorted_files.clear();
       std::queue<FileOperation> file_operations;
 
       // Populate the queue with all files in the ResourceTable.
       for (auto& entry : type->entries) {
         for (auto& config_value : entry->values) {
+          // WARNING! Do not insert or remove any resources while executing in this scope. It will
+          // corrupt the iteration order.
+
           FileReference* file_ref = ValueCast<FileReference>(config_value->value.get());
           if (!file_ref) {
             continue;
@@ -497,6 +531,7 @@
           file_op.entry = entry.get();
           file_op.dst_path = *file_ref->path;
           file_op.config = config_value->config;
+          file_op.file_to_copy = file;
 
           const StringPiece src_path = file->GetSource().path;
           if (type->type != ResourceType::kRaw &&
@@ -518,68 +553,51 @@
             file_op.xml_to_flatten->file.config = config_value->config;
             file_op.xml_to_flatten->file.source = file_ref->GetSource();
             file_op.xml_to_flatten->file.name = ResourceName(pkg->name, type->type, entry->name);
-
-            // Enqueue the XML files to be processed.
-            file_operations.push(std::move(file_op));
-          } else {
-            file_op.file_to_copy = file;
-
-            // NOTE(adamlesinski): Explicitly construct a StringPiece here, or
-            // else we end up copying the string in the std::make_pair() method,
-            // then creating a StringPiece from the copy, which would cause us
-            // to end up referencing garbage in the map.
-            const StringPiece entry_name(entry->name);
-            config_sorted_files[std::make_pair(config_value->config, entry_name)] =
-                std::move(file_op);
           }
+
+          // NOTE(adamlesinski): Explicitly construct a StringPiece here, or
+          // else we end up copying the string in the std::make_pair() method,
+          // then creating a StringPiece from the copy, which would cause us
+          // to end up referencing garbage in the map.
+          const StringPiece entry_name(entry->name);
+          config_sorted_files[std::make_pair(config_value->config, entry_name)] =
+              std::move(file_op);
         }
       }
 
-      // Now process the XML queue
-      for (; !file_operations.empty(); file_operations.pop()) {
-        FileOperation& file_op = file_operations.front();
-
-        if (!LinkAndVersionXmlFile(table, &file_op, &file_operations)) {
-          error = true;
-          continue;
-        }
-
-        // NOTE(adamlesinski): Explicitly construct a StringPiece here, or else
-        // we end up copying the string in the std::make_pair() method, then
-        // creating a StringPiece from the copy, which would cause us to end up
-        // referencing garbage in the map.
-        const StringPiece entry_name(file_op.entry->name);
-        config_sorted_files[std::make_pair(file_op.config, entry_name)] = std::move(file_op);
-      }
-
-      if (error) {
-        return false;
-      }
-
       // Now flatten the sorted values.
       for (auto& map_entry : config_sorted_files) {
         const ConfigDescription& config = map_entry.first.first;
-        const FileOperation& file_op = map_entry.second;
+        FileOperation& file_op = map_entry.second;
 
         if (file_op.xml_to_flatten) {
-          Maybe<size_t> max_sdk_level;
-          if (!options_.no_auto_version && !file_op.skip_version) {
-            max_sdk_level = std::max<size_t>(std::max<size_t>(config.sdkVersion, 1u),
-                                             context_->GetMinSdkVersion());
-          }
+          std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
+              LinkAndVersionXmlFile(table, &file_op);
+          for (std::unique_ptr<xml::XmlResource>& doc : versioned_docs) {
+            std::string dst_path = file_op.dst_path;
+            if (doc->file.config != file_op.config) {
+              // Only add the new versioned configurations.
+              if (context_->IsVerbose()) {
+                context_->GetDiagnostics()->Note(DiagMessage(doc->file.source)
+                                                 << "auto-versioning resource from config '"
+                                                 << config << "' -> '" << doc->file.config << "'");
+              }
 
-          bool result = FlattenXml(file_op.xml_to_flatten.get(), file_op.dst_path, max_sdk_level,
-                                   options_.keep_raw_values, archive_writer, context_);
-          if (!result) {
-            error = true;
+              dst_path =
+                  ResourceUtils::BuildResourceFileName(doc->file, context_->GetNameMangler());
+              bool result = table->AddFileReferenceAllowMangled(doc->file.name, doc->file.config,
+                                                                doc->file.source, dst_path, nullptr,
+                                                                context_->GetDiagnostics());
+              if (!result) {
+                return false;
+              }
+            }
+            error |= !FlattenXml(context_, doc.get(), dst_path, options_.keep_raw_values,
+                                 archive_writer);
           }
         } else {
-          bool result =
-              io::CopyFileToArchive(context_, file_op.file_to_copy, file_op.dst_path,
-                                    GetCompressionFlags(file_op.dst_path), archive_writer);
-          if (!result) {
-            error = true;
-          }
+          error |= !io::CopyFileToArchive(context_, file_op.file_to_copy, file_op.dst_path,
+                                          GetCompressionFlags(file_op.dst_path), archive_writer);
         }
       }
     }
@@ -614,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;
   }
@@ -1358,8 +1376,7 @@
   bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
                 ResourceTable* table) {
     const bool keep_raw_values = context_->GetPackageType() == PackageType::kStaticLib;
-    bool result =
-        FlattenXml(manifest, "AndroidManifest.xml", {}, keep_raw_values, writer, context_);
+    bool result = FlattenXml(context_, manifest, "AndroidManifest.xml", keep_raw_values, writer);
     if (!result) {
       return false;
     }
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index 14d4260..8741b7b 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -131,7 +131,7 @@
 }
 
 static xml::AaptAttribute CreateAttributeWithId(const ResourceId& id) {
-  return xml::AaptAttribute{id, Attribute(true)};
+  return xml::AaptAttribute(Attribute(), id);
 }
 
 std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
index e98d2d7..0711749 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -194,16 +194,9 @@
 
     // Filter the attributes.
     for (xml::Attribute& attr : node->attributes) {
-      if (options_.max_sdk_level && attr.compiled_attribute && attr.compiled_attribute.value().id) {
-        size_t sdk_level = FindAttributeSdkLevel(attr.compiled_attribute.value().id.value());
-        if (sdk_level > options_.max_sdk_level.value()) {
-          continue;
-        }
+      if (attr.namespace_uri != xml::kSchemaTools) {
+        filtered_attrs_.push_back(&attr);
       }
-      if (attr.namespace_uri == xml::kSchemaTools) {
-        continue;
-      }
-      filtered_attrs_.push_back(&attr);
     }
 
     if (filtered_attrs_.empty()) {
diff --git a/tools/aapt2/flatten/XmlFlattener.h b/tools/aapt2/flatten/XmlFlattener.h
index f5129fd..87557f2 100644
--- a/tools/aapt2/flatten/XmlFlattener.h
+++ b/tools/aapt2/flatten/XmlFlattener.h
@@ -30,11 +30,6 @@
    * Keep attribute raw string values along with typed values.
    */
   bool keep_raw_values = false;
-
-  /**
-   * If set, the max SDK level of attribute to flatten. All others are ignored.
-   */
-  Maybe<size_t> max_sdk_level;
 };
 
 class XmlFlattener : public IXmlResourceConsumer {
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
index cfa89bb..f1e903f 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -149,31 +149,6 @@
   ASSERT_EQ(android::ResXMLTree::END_DOCUMENT, tree.next());
 }
 
-TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) {
-  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
-            <View xmlns:android="http://schemas.android.com/apk/res/android"
-                android:paddingStart="1dp"
-                android:colorAccent="#ffffff"/>)EOF");
-
-  XmlReferenceLinker linker;
-  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
-  ASSERT_TRUE(linker.sdk_levels().count(17) == 1);
-  ASSERT_TRUE(linker.sdk_levels().count(21) == 1);
-
-  android::ResXMLTree tree;
-  XmlFlattenerOptions options;
-  options.max_sdk_level = 17;
-  ASSERT_TRUE(Flatten(doc.get(), &tree, options));
-
-  while (tree.next() != android::ResXMLTree::START_TAG) {
-    ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
-    ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
-  }
-
-  ASSERT_EQ(1u, tree.getAttributeCount());
-  EXPECT_EQ(uint32_t(0x010103b3), tree.getAttributeNameResID(0));
-}
-
 TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripOnlyTools) {
   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
             <View xmlns:tools="http://schemas.android.com/tools"
@@ -234,13 +209,11 @@
   }
 
   const StringPiece16 kPackage = u"package";
-  EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()),
-            0);
+  EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0);
 }
 
 TEST_F(XmlFlattenerTest, EmptyStringValueInAttributeIsNotNull) {
-  std::unique_ptr<xml::XmlResource> doc =
-      test::BuildXmlDom("<View package=\"\"/>");
+  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom("<View package=\"\"/>");
 
   android::ResXMLTree tree;
   ASSERT_TRUE(Flatten(doc.get(), &tree));
@@ -251,8 +224,7 @@
   }
 
   const StringPiece16 kPackage = u"package";
-  ssize_t idx =
-      tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size());
+  ssize_t idx = tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size());
   ASSERT_GE(idx, 0);
 
   size_t len;
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/Android.mk b/tools/aapt2/integration-tests/AutoVersionTest/Android.mk
new file mode 100644
index 0000000..012728f
--- /dev/null
+++ b/tools/aapt2/integration-tests/AutoVersionTest/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 := AaptAutoVersionTest
+LOCAL_MODULE_TAGS := tests
+include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/AndroidManifest.xml b/tools/aapt2/integration-tests/AutoVersionTest/AndroidManifest.xml
new file mode 100644
index 0000000..e66d709
--- /dev/null
+++ b/tools/aapt2/integration-tests/AutoVersionTest/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?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.autoversiontest">
+
+    <uses-sdk android:minSdkVersion="7" />
+</manifest>
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/res/layout-v21/layout3.xml b/tools/aapt2/integration-tests/AutoVersionTest/res/layout-v21/layout3.xml
new file mode 100644
index 0000000..bfc5445
--- /dev/null
+++ b/tools/aapt2/integration-tests/AutoVersionTest/res/layout-v21/layout3.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:paddingVertical="24dp" />
+ 
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/res/layout/layout.xml b/tools/aapt2/integration-tests/AutoVersionTest/res/layout/layout.xml
new file mode 100644
index 0000000..091a8ce
--- /dev/null
+++ b/tools/aapt2/integration-tests/AutoVersionTest/res/layout/layout.xml
@@ -0,0 +1,35 @@
+<?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:autoStart="true"
+    android:expandableListViewWhiteStyle="@empty"
+    android:screenSize="large"
+    android:subtitle="Hello"
+    android:resizeMode="none"
+    android:largestWidthLimitDp="999"
+    android:uiOptions="none"
+    android:parentActivityName="Hello"
+    android:paddingStart="999dp"
+    android:requiredForAllUsers="true"
+    android:category="Hello"
+    android:isGame="true"
+    android:colorPrimary="#ffffff"
+    android:revisionCode="999"
+    android:autoVerify="true"
+    android:use32bitAbi="true"
+    android:shortcutId="@+id/id" />
+
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/res/layout/layout2.xml b/tools/aapt2/integration-tests/AutoVersionTest/res/layout/layout2.xml
new file mode 100644
index 0000000..339337a
--- /dev/null
+++ b/tools/aapt2/integration-tests/AutoVersionTest/res/layout/layout2.xml
@@ -0,0 +1,47 @@
+<?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"
+    class="foo">
+    <View
+        android:paddingHorizontal="24dp" />
+    <View
+        android:paddingHorizontal="24dp"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp" />
+    <View
+        android:paddingHorizontal="24dp"
+        android:paddingLeft="16dp"
+        android:paddingRight="16dp" />
+    <View
+        android:paddingVertical="24dp" />
+    <View
+        android:paddingVertical="24dp"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp" />
+    <View
+        android:layout_marginHorizontal="24dp" />
+    <View
+        android:layout_marginHorizontal="24dp"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="16dp" />
+    <View
+        android:layout_marginVertical="24dp" />
+    <View
+        android:layout_marginVertical="24dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginBottom="16dp" />
+</View>
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/res/layout/layout3.xml b/tools/aapt2/integration-tests/AutoVersionTest/res/layout/layout3.xml
new file mode 100644
index 0000000..8025ce1
--- /dev/null
+++ b/tools/aapt2/integration-tests/AutoVersionTest/res/layout/layout3.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:paddingHorizontal="24dp" />
+ 
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/res/values-v21/styles.xml b/tools/aapt2/integration-tests/AutoVersionTest/res/values-v21/styles.xml
new file mode 100644
index 0000000..ff13faa
--- /dev/null
+++ b/tools/aapt2/integration-tests/AutoVersionTest/res/values-v21/styles.xml
@@ -0,0 +1,28 @@
+<?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>
+    <style name="Style2">
+        <!-- API 7 -->
+        <item name="android:autoStart">false</item>
+
+        <!-- API 17 -->
+        <item name="android:paddingStart">0dp</item>
+
+        <!-- API 21 -->
+        <item name="android:colorPrimary">#00ff00</item>
+    </style>
+</resources>
diff --git a/tools/aapt2/integration-tests/AutoVersionTest/res/values/styles.xml b/tools/aapt2/integration-tests/AutoVersionTest/res/values/styles.xml
new file mode 100644
index 0000000..c4f09846
--- /dev/null
+++ b/tools/aapt2/integration-tests/AutoVersionTest/res/values/styles.xml
@@ -0,0 +1,84 @@
+<?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>
+    <style name="Style1">
+        <!-- API 7 -->
+        <item name="android:autoStart">true</item>
+
+        <!-- API 8 -->
+        <item name="android:expandableListViewWhiteStyle">@null</item>
+
+        <!-- API 9 -->
+        <item name="android:screenSize">large</item>
+
+        <!-- API 11 -->
+        <item name="android:subtitle">Hello</item>
+
+        <!-- API 12 -->
+        <item name="android:resizeMode">none</item>
+
+        <!-- API 13 -->
+        <item name="android:largestWidthLimitDp">999</item>
+
+        <!-- API 14 -->
+        <item name="android:uiOptions">none</item>
+
+        <!-- API 16 -->
+        <item name="android:parentActivityName">Hello</item>
+
+        <!-- API 17 -->
+        <item name="android:paddingStart">999dp</item>
+
+        <!-- API 18 -->
+        <item name="android:requiredForAllUsers">true</item>
+
+        <!-- API 19 -->
+        <item name="android:category">Hello</item>
+
+        <!-- API 20 -->
+        <item name="android:isGame">true</item>
+
+        <!-- API 21 -->
+        <item name="android:colorPrimary">#ffffff</item>
+
+        <!-- API 22 -->
+        <item name="android:revisionCode">999</item>
+
+        <!-- API 23 -->
+        <item name="android:autoVerify">true</item>
+
+        <!-- API 24 -->
+        <item name="android:use32bitAbi">true</item>
+
+        <!-- API 25 -->
+        <item name="android:shortcutId">@+id/id</item>
+
+        <!-- API 26 -->
+        <item name="android:paddingHorizontal">999dp</item>
+    </style>
+
+    <style name="Style2">
+        <!-- API 7 -->
+        <item name="android:autoStart">true</item>
+
+        <!-- API 17 -->
+        <item name="android:paddingStart">999dp</item>
+
+        <!-- API 21 -->
+        <item name="android:colorPrimary">#ffffff</item>
+    </style>
+</resources>
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/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index 77471ea..f80c6e9 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -27,13 +27,15 @@
 
 namespace aapt {
 
-bool ShouldGenerateVersionedResource(const ResourceEntry* entry,
-                                     const ConfigDescription& config,
-                                     const int sdk_version_to_generate) {
-  // We assume the caller is trying to generate a version greater than the
-  // current configuration.
+bool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
+                                     const ApiVersion sdk_version_to_generate) {
+  // We assume the caller is trying to generate a version greater than the current configuration.
   CHECK(sdk_version_to_generate > config.sdkVersion);
+  return sdk_version_to_generate < FindNextApiVersionForConfig(entry, config);
+}
 
+ApiVersion FindNextApiVersionForConfig(const ResourceEntry* entry,
+                                       const ConfigDescription& config) {
   const auto end_iter = entry->values.end();
   auto iter = entry->values.begin();
   for (; iter != end_iter; ++iter) {
@@ -46,26 +48,23 @@
   CHECK(iter != entry->values.end());
   ++iter;
 
-  // The next configuration either only varies in sdkVersion, or it is
-  // completely different
-  // and therefore incompatible. If it is incompatible, we must generate the
-  // versioned resource.
+  // The next configuration either only varies in sdkVersion, or it is completely different
+  // and therefore incompatible. If it is incompatible, we must generate the versioned resource.
 
-  // NOTE: The ordering of configurations takes sdkVersion as higher precedence
-  // than other
+  // NOTE: The ordering of configurations takes sdkVersion as higher precedence than other
   // qualifiers, so we need to iterate through the entire list to be sure there
   // are no higher sdk level versions of this resource.
   ConfigDescription temp_config(config);
   for (; iter != end_iter; ++iter) {
     temp_config.sdkVersion = (*iter)->config.sdkVersion;
     if (temp_config == (*iter)->config) {
-      // The two configs are the same, check the sdk version.
-      return sdk_version_to_generate < (*iter)->config.sdkVersion;
+      // The two configs are the same, return the sdkVersion.
+      return (*iter)->config.sdkVersion;
     }
   }
 
-  // No match was found, so we should generate the versioned resource.
-  return true;
+  // Didn't find another config with a different sdk version, so return the highest possible value.
+  return std::numeric_limits<ApiVersion>::max();
 }
 
 bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
@@ -86,7 +85,7 @@
           }
 
           if (Style* style = ValueCast<Style>(config_value->value.get())) {
-            Maybe<size_t> min_sdk_stripped;
+            Maybe<ApiVersion> min_sdk_stripped;
             std::vector<Style::Entry> stripped;
 
             auto iter = style->entries.begin();
@@ -95,17 +94,14 @@
 
               // Find the SDK level that is higher than the configuration
               // allows.
-              const size_t sdk_level =
-                  FindAttributeSdkLevel(iter->key.id.value());
-              if (sdk_level >
-                  std::max<size_t>(config_value->config.sdkVersion, 1)) {
+              const ApiVersion sdk_level = FindAttributeSdkLevel(iter->key.id.value());
+              if (sdk_level > std::max<ApiVersion>(config_value->config.sdkVersion, 1)) {
                 // Record that we are about to strip this.
                 stripped.emplace_back(std::move(*iter));
 
                 // We use the smallest SDK level to generate the new style.
                 if (min_sdk_stripped) {
-                  min_sdk_stripped =
-                      std::min(min_sdk_stripped.value(), sdk_level);
+                  min_sdk_stripped = std::min(min_sdk_stripped.value(), sdk_level);
                 } else {
                   min_sdk_stripped = sdk_level;
                 }
@@ -126,10 +122,9 @@
                                                   min_sdk_stripped.value())) {
                 // Let's create a new Style for this versioned resource.
                 ConfigDescription new_config(config_value->config);
-                new_config.sdkVersion = min_sdk_stripped.value();
+                new_config.sdkVersion = static_cast<uint16_t>(min_sdk_stripped.value());
 
-                std::unique_ptr<Style> new_style(
-                    style->Clone(&table->string_pool));
+                std::unique_ptr<Style> new_style(style->Clone(&table->string_pool));
                 new_style->SetComment(style->GetComment());
                 new_style->SetSource(style->GetSource());
 
@@ -140,8 +135,7 @@
                     std::make_move_iterator(stripped.end()));
 
                 // Insert the new Resource into the correct place.
-                entry->FindOrCreateValue(new_config, {})->value =
-                    std::move(new_style);
+                entry->FindOrCreateValue(new_config, {})->value = std::move(new_style);
               }
             }
           }
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index d00fa73..5527f90 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -23,6 +23,7 @@
 #include "android-base/macros.h"
 
 #include "Resource.h"
+#include "SdkConstants.h"
 #include "process/IResourceTableConsumer.h"
 #include "xml/XmlDom.h"
 
@@ -44,9 +45,12 @@
  * Determines whether a versioned resource should be created. If a versioned
  * resource already exists, it takes precedence.
  */
-bool ShouldGenerateVersionedResource(const ResourceEntry* entry,
-                                     const ConfigDescription& config,
-                                     const int sdk_version_to_generate);
+bool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
+                                     const ApiVersion sdk_version_to_generate);
+
+// Finds the next largest ApiVersion of the config which is identical to the given config except
+// for sdkVersion.
+ApiVersion FindNextApiVersionForConfig(const ResourceEntry* entry, const ConfigDescription& config);
 
 class AutoVersioner : public IResourceTableConsumer {
  public:
@@ -105,11 +109,10 @@
 
 class ProductFilter : public IResourceTableConsumer {
  public:
-  using ResourceConfigValueIter =
-      std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
+  using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
 
-  explicit ProductFilter(std::unordered_set<std::string> products)
-      : products_(products) {}
+  explicit ProductFilter(std::unordered_set<std::string> products) : products_(products) {
+  }
 
   ResourceConfigValueIter SelectProductToKeep(
       const ResourceNameRef& name, const ResourceConfigValueIter begin,
@@ -118,19 +121,9 @@
   bool Consume(IAaptContext* context, ResourceTable* table) override;
 
  private:
-  std::unordered_set<std::string> products_;
-
   DISALLOW_COPY_AND_ASSIGN(ProductFilter);
-};
 
-class XmlAutoVersioner : public IXmlResourceConsumer {
- public:
-  XmlAutoVersioner() = default;
-
-  bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(XmlAutoVersioner);
+  std::unordered_set<std::string> products_;
 };
 
 /**
@@ -143,8 +136,7 @@
  */
 class XmlNamespaceRemover : public IXmlResourceConsumer {
  public:
-  explicit XmlNamespaceRemover(bool keep_uris = false)
-      : keep_uris_(keep_uris){};
+  explicit XmlNamespaceRemover(bool keep_uris = false) : keep_uris_(keep_uris){};
 
   bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
 
@@ -165,17 +157,8 @@
 
   bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
 
-  /**
-   * Once the XmlResource has been consumed, this returns the various SDK levels
-   * in which
-   * framework attributes used within the XML document were defined.
-   */
-  inline const std::set<int>& sdk_levels() const { return sdk_levels_found_; }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(XmlReferenceLinker);
-
-  std::set<int> sdk_levels_found_;
 };
 
 }  // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 833ae69..18498e3 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -274,7 +274,7 @@
     if (out_error) *out_error = "is not an attribute";
     return {};
   }
-  return xml::AaptAttribute{symbol->id, *symbol->attribute};
+  return xml::AaptAttribute(*symbol->attribute, symbol->id);
 }
 
 void ReferenceLinker::WriteResourceName(DiagMessage* out_msg,
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 9311091..cce750a 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/link/XmlCompatVersioner.cpp b/tools/aapt2/link/XmlCompatVersioner.cpp
new file mode 100644
index 0000000..f1f4e3b
--- /dev/null
+++ b/tools/aapt2/link/XmlCompatVersioner.cpp
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+#include "link/XmlCompatVersioner.h"
+
+#include <algorithm>
+
+#include "util/Util.h"
+
+namespace aapt {
+
+static xml::Attribute CopyAttr(const xml::Attribute& src, StringPool* out_string_pool) {
+  xml::Attribute dst{src.namespace_uri, src.name, src.value, src.compiled_attribute};
+  if (src.compiled_value != nullptr) {
+    dst.compiled_value.reset(src.compiled_value->Clone(out_string_pool));
+  }
+  return dst;
+}
+
+// Returns false if the attribute is not copied because an existing attribute takes precedence
+// (came from a rule).
+static bool CopyAttribute(const xml::Attribute& src_attr, bool generated, xml::Element* dst_el,
+                          StringPool* out_string_pool) {
+  xml::Attribute* dst_attr = dst_el->FindAttribute(src_attr.namespace_uri, src_attr.name);
+  if (dst_attr != nullptr) {
+    if (generated) {
+      // Generated attributes always take precedence.
+      dst_attr->value = src_attr.value;
+      dst_attr->compiled_attribute = src_attr.compiled_attribute;
+      if (src_attr.compiled_value != nullptr) {
+        dst_attr->compiled_value.reset(src_attr.compiled_value->Clone(out_string_pool));
+      }
+      return true;
+    }
+    return false;
+  }
+  dst_el->attributes.push_back(CopyAttr(src_attr, out_string_pool));
+  return true;
+}
+
+void XmlCompatVersioner::ProcessRule(const xml::Element& src_el, const xml::Attribute& src_attr,
+                                     const ApiVersion& src_attr_version, const IDegradeRule* rule,
+                                     const util::Range<ApiVersion>& api_range, bool generated,
+                                     xml::Element* dst_el,
+                                     std::set<ApiVersion>* out_apis_referenced,
+                                     StringPool* out_string_pool) {
+  if (src_attr_version <= api_range.start) {
+    // The API is compatible, so don't check the rule and just copy.
+    if (!CopyAttribute(src_attr, generated, dst_el, out_string_pool)) {
+      // TODO(adamlesinski): Log a warning that an attribute was overridden?
+    }
+    return;
+  }
+
+  if (api_range.start >= SDK_LOLLIPOP_MR1) {
+    // Since LOLLIPOP MR1, the framework can handle silently ignoring unknown public attributes,
+    // so we don't need to erase/version them.
+    // Copy.
+    if (!CopyAttribute(src_attr, generated, dst_el, out_string_pool)) {
+      // TODO(adamlesinski): Log a warning that an attribute was overridden?
+    }
+  } else {
+    // We are going to erase this attribute from this XML resource version, but check if
+    // we even need to move it anywhere. A developer may have effectively overwritten it with
+    // a similarly versioned XML resource.
+    if (src_attr_version < api_range.end) {
+      // There is room for another versioned XML resource between this XML resource and the next
+      // versioned XML resource defined by the developer.
+      out_apis_referenced->insert(std::min<ApiVersion>(src_attr_version, SDK_LOLLIPOP_MR1));
+    }
+  }
+
+  if (rule != nullptr) {
+    for (const DegradeResult& result : rule->Degrade(src_el, src_attr, out_string_pool)) {
+      const ResourceId attr_resid = result.attr.compiled_attribute.value().id.value();
+      const ApiVersion attr_version = FindAttributeSdkLevel(attr_resid);
+
+      auto iter = rules_->find(attr_resid);
+      ProcessRule(src_el, result.attr, attr_version,
+                  iter != rules_->end() ? iter->second.get() : nullptr, api_range,
+                  true /*generated*/, dst_el, out_apis_referenced, out_string_pool);
+    }
+  }
+}
+
+XmlCompatVersioner::XmlCompatVersioner(const Rules* rules) : rules_(rules) {
+}
+
+std::unique_ptr<xml::XmlResource> XmlCompatVersioner::ProcessDoc(
+    ApiVersion target_api, ApiVersion max_api, xml::XmlResource* doc,
+    std::set<ApiVersion>* out_apis_referenced) {
+  const util::Range<ApiVersion> api_range{target_api, max_api};
+
+  std::unique_ptr<xml::XmlResource> cloned_doc = util::make_unique<xml::XmlResource>(doc->file);
+  cloned_doc->file.config.sdkVersion = static_cast<uint16_t>(target_api);
+
+  cloned_doc->root = doc->root->Clone([&](const xml::Element& el, xml::Element* out_el) {
+    for (const auto& attr : el.attributes) {
+      if (!attr.compiled_attribute) {
+        // Just copy if this isn't a compiled attribute.
+        out_el->attributes.push_back(CopyAttr(attr, &cloned_doc->string_pool));
+        continue;
+      }
+
+      const ResourceId attr_resid = attr.compiled_attribute.value().id.value();
+      const ApiVersion attr_version = FindAttributeSdkLevel(attr_resid);
+
+      auto rule = rules_->find(attr_resid);
+      ProcessRule(el, attr, attr_version, rule != rules_->end() ? rule->second.get() : nullptr,
+                  api_range, false /*generated*/, out_el, out_apis_referenced,
+                  &cloned_doc->string_pool);
+    }
+  });
+  return cloned_doc;
+}
+
+std::vector<std::unique_ptr<xml::XmlResource>> XmlCompatVersioner::Process(
+    IAaptContext* context, xml::XmlResource* doc, util::Range<ApiVersion> api_range) {
+  // Adjust the API range so that it falls after this document and after minSdkVersion.
+  api_range.start = std::max(api_range.start, context->GetMinSdkVersion());
+  api_range.start = std::max(api_range.start, static_cast<ApiVersion>(doc->file.config.sdkVersion));
+
+  std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs;
+  std::set<ApiVersion> apis_referenced;
+  versioned_docs.push_back(ProcessDoc(api_range.start, api_range.end, doc, &apis_referenced));
+
+  // Adjust the sdkVersion of the first XML document back to its original (this only really
+  // makes a difference if the sdk version was below the minSdk to start).
+  versioned_docs.back()->file.config.sdkVersion = doc->file.config.sdkVersion;
+
+  // Iterate from smallest to largest API version.
+  for (ApiVersion api : apis_referenced) {
+    std::set<ApiVersion> dummy;
+    versioned_docs.push_back(ProcessDoc(api, api_range.end, doc, &dummy));
+  }
+  return versioned_docs;
+}
+
+DegradeToManyRule::DegradeToManyRule(std::vector<ReplacementAttr> attrs)
+    : attrs_(std::move(attrs)) {
+}
+
+static inline std::unique_ptr<Item> CloneIfNotNull(const std::unique_ptr<Item>& src,
+                                                   StringPool* out_string_pool) {
+  if (src == nullptr) {
+    return {};
+  }
+  return std::unique_ptr<Item>(src->Clone(out_string_pool));
+}
+
+std::vector<DegradeResult> DegradeToManyRule::Degrade(const xml::Element& src_el,
+                                                      const xml::Attribute& src_attr,
+                                                      StringPool* out_string_pool) const {
+  std::vector<DegradeResult> result;
+  result.reserve(attrs_.size());
+  for (const ReplacementAttr& attr : attrs_) {
+    result.push_back(
+        DegradeResult{xml::Attribute{xml::kSchemaAndroid, attr.name, src_attr.value,
+                                     xml::AaptAttribute{attr.attr, attr.id},
+                                     CloneIfNotNull(src_attr.compiled_value, out_string_pool)},
+                      FindAttributeSdkLevel(attr.id)});
+  }
+  return result;
+}
+
+}  // namespace aapt
diff --git a/tools/aapt2/link/XmlCompatVersioner.h b/tools/aapt2/link/XmlCompatVersioner.h
new file mode 100644
index 0000000..099e23c
--- /dev/null
+++ b/tools/aapt2/link/XmlCompatVersioner.h
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_LINKER_XMLCOMPATVERSIONER_H
+#define AAPT_LINKER_XMLCOMPATVERSIONER_H
+
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "android-base/macros.h"
+
+#include "Resource.h"
+#include "SdkConstants.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/Util.h"
+#include "xml/XmlDom.h"
+
+namespace aapt {
+
+class IDegradeRule;
+
+struct DegradeResult {
+  xml::Attribute attr;
+  ApiVersion attr_api_version;
+};
+
+class IDegradeRule {
+ public:
+  IDegradeRule() = default;
+  virtual ~IDegradeRule() = default;
+
+  virtual std::vector<DegradeResult> Degrade(const xml::Element& src_el,
+                                             const xml::Attribute& src_attr,
+                                             StringPool* out_string_pool) const = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(IDegradeRule);
+};
+
+class XmlCompatVersioner {
+ public:
+  using Rules = std::unordered_map<ResourceId, std::unique_ptr<IDegradeRule>>;
+
+  XmlCompatVersioner(const Rules* rules);
+
+  std::vector<std::unique_ptr<xml::XmlResource>> Process(IAaptContext* context,
+                                                         xml::XmlResource* doc,
+                                                         util::Range<ApiVersion> api_range);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(XmlCompatVersioner);
+
+  std::unique_ptr<xml::XmlResource> ProcessDoc(ApiVersion target_api, ApiVersion max_api,
+                                               xml::XmlResource* doc,
+                                               std::set<ApiVersion>* out_apis_referenced);
+  void ProcessRule(const xml::Element& src_el, const xml::Attribute& src_attr,
+                   const ApiVersion& src_attr_version, const IDegradeRule* rule,
+                   const util::Range<ApiVersion>& api_range, bool generated, xml::Element* dst_el,
+                   std::set<ApiVersion>* out_apis_referenced, StringPool* out_string_pool);
+
+  const Rules* rules_;
+};
+
+struct ReplacementAttr {
+  std::string name;
+  ResourceId id;
+  Attribute attr;
+};
+
+class DegradeToManyRule : public IDegradeRule {
+ public:
+  DegradeToManyRule(std::vector<ReplacementAttr> attrs);
+  virtual ~DegradeToManyRule() = default;
+
+  std::vector<DegradeResult> Degrade(const xml::Element& src_el, const xml::Attribute& src_attr,
+                                     StringPool* out_string_pool) const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DegradeToManyRule);
+
+  std::vector<ReplacementAttr> attrs_;
+};
+
+}  // namespace aapt
+
+#endif  // AAPT_LINKER_XMLCOMPATVERSIONER_H
diff --git a/tools/aapt2/link/XmlCompatVersioner_test.cpp b/tools/aapt2/link/XmlCompatVersioner_test.cpp
new file mode 100644
index 0000000..ce6605c
--- /dev/null
+++ b/tools/aapt2/link/XmlCompatVersioner_test.cpp
@@ -0,0 +1,320 @@
+/*
+ * 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.
+ */
+
+#include "link/XmlCompatVersioner.h"
+
+#include "Linkers.h"
+#include "test/Test.h"
+
+namespace aapt {
+
+constexpr auto TYPE_DIMENSION = android::ResTable_map::TYPE_DIMENSION;
+constexpr auto TYPE_STRING = android::ResTable_map::TYPE_STRING;
+
+struct R {
+  struct attr {
+    enum : uint32_t {
+      paddingLeft = 0x010100d6u,         // (API 1)
+      paddingRight = 0x010100d8u,        // (API 1)
+      progressBarPadding = 0x01010319u,  // (API 11)
+      paddingStart = 0x010103b3u,        // (API 17)
+      paddingHorizontal = 0x0101053du,   // (API 26)
+    };
+  };
+};
+
+class XmlCompatVersionerTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    context_ =
+        test::ContextBuilder()
+            .SetCompilationPackage("com.app")
+            .SetPackageId(0x7f)
+            .SetPackageType(PackageType::kApp)
+            .SetMinSdkVersion(SDK_GINGERBREAD)
+            .AddSymbolSource(
+                test::StaticSymbolSourceBuilder()
+                    .AddPublicSymbol("android:attr/paddingLeft", R::attr::paddingLeft,
+                                     util::make_unique<Attribute>(false, TYPE_DIMENSION))
+                    .AddPublicSymbol("android:attr/paddingRight", R::attr::paddingRight,
+                                     util::make_unique<Attribute>(false, TYPE_DIMENSION))
+                    .AddPublicSymbol("android:attr/progressBarPadding", R::attr::progressBarPadding,
+                                     util::make_unique<Attribute>(false, TYPE_DIMENSION))
+                    .AddPublicSymbol("android:attr/paddingStart", R::attr::paddingStart,
+                                     util::make_unique<Attribute>(false, TYPE_DIMENSION))
+                    .AddPublicSymbol("android:attr/paddingHorizontal", R::attr::paddingHorizontal,
+                                     util::make_unique<Attribute>(false, TYPE_DIMENSION))
+                    .AddSymbol("com.app:attr/foo", ResourceId(0x7f010000),
+                               util::make_unique<Attribute>(false, TYPE_STRING))
+                    .Build())
+            .Build();
+  }
+
+ protected:
+  std::unique_ptr<IAaptContext> context_;
+};
+
+TEST_F(XmlCompatVersionerTest, NoRulesOnlyStripsAndCopies) {
+  auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
+      <View xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:app="http://schemas.android.com/apk/res-auto"
+          android:paddingHorizontal="24dp"
+          app:foo="16dp"
+          foo="bar"/>)EOF");
+
+  XmlReferenceLinker linker;
+  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
+
+  XmlCompatVersioner::Rules rules;
+  const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1};
+
+  XmlCompatVersioner versioner(&rules);
+  std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
+      versioner.Process(context_.get(), doc.get(), api_range);
+  ASSERT_EQ(2u, versioned_docs.size());
+
+  xml::Element* el;
+
+  // Source XML file's sdkVersion == 0, so the first one must also have the same sdkVersion.
+  EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion);
+  el = xml::FindRootElement(versioned_docs[0].get());
+  ASSERT_NE(nullptr, el);
+  EXPECT_EQ(2u, el->attributes.size());
+  EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
+  EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo"));
+  EXPECT_NE(nullptr, el->FindAttribute({}, "foo"));
+
+  EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[1]->file.config.sdkVersion);
+  el = xml::FindRootElement(versioned_docs[1].get());
+  ASSERT_NE(nullptr, el);
+  EXPECT_EQ(3u, el->attributes.size());
+  EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
+  EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo"));
+  EXPECT_NE(nullptr, el->FindAttribute({}, "foo"));
+}
+
+TEST_F(XmlCompatVersionerTest, SingleRule) {
+  auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
+      <View xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:app="http://schemas.android.com/apk/res-auto"
+          android:paddingHorizontal="24dp"
+          app:foo="16dp"
+          foo="bar"/>)EOF");
+
+  XmlReferenceLinker linker;
+  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
+
+  XmlCompatVersioner::Rules rules;
+  rules[R::attr::paddingHorizontal] =
+      util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>(
+          {ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(false, TYPE_DIMENSION)},
+           ReplacementAttr{"paddingRight", R::attr::paddingRight,
+                           Attribute(false, TYPE_DIMENSION)}}));
+
+  const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1};
+
+  XmlCompatVersioner versioner(&rules);
+  std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
+      versioner.Process(context_.get(), doc.get(), api_range);
+  ASSERT_EQ(2u, versioned_docs.size());
+
+  xml::Element* el;
+
+  EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion);
+  el = xml::FindRootElement(versioned_docs[0].get());
+  ASSERT_NE(nullptr, el);
+  EXPECT_EQ(4u, el->attributes.size());
+  EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
+  EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo"));
+  EXPECT_NE(nullptr, el->FindAttribute({}, "foo"));
+
+  xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(attr->compiled_attribute);
+
+  attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(attr->compiled_attribute);
+
+  EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[1]->file.config.sdkVersion);
+  el = xml::FindRootElement(versioned_docs[1].get());
+  ASSERT_NE(nullptr, el);
+  EXPECT_EQ(5u, el->attributes.size());
+  EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
+  EXPECT_NE(nullptr, el->FindAttribute(xml::kSchemaAuto, "foo"));
+  EXPECT_NE(nullptr, el->FindAttribute({}, "foo"));
+
+  attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(attr->compiled_attribute);
+
+  attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(attr->compiled_attribute);
+}
+
+TEST_F(XmlCompatVersionerTest, ChainedRule) {
+  auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
+      <View xmlns:android="http://schemas.android.com/apk/res/android"
+          android:paddingHorizontal="24dp" />)EOF");
+
+  XmlReferenceLinker linker;
+  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
+
+  XmlCompatVersioner::Rules rules;
+  rules[R::attr::progressBarPadding] =
+      util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>(
+          {ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(false, TYPE_DIMENSION)},
+           ReplacementAttr{"paddingRight", R::attr::paddingRight,
+                           Attribute(false, TYPE_DIMENSION)}}));
+  rules[R::attr::paddingHorizontal] =
+      util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>({ReplacementAttr{
+          "progressBarPadding", R::attr::progressBarPadding, Attribute(false, TYPE_DIMENSION)}}));
+
+  const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1};
+
+  XmlCompatVersioner versioner(&rules);
+  std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
+      versioner.Process(context_.get(), doc.get(), api_range);
+  ASSERT_EQ(3u, versioned_docs.size());
+
+  xml::Element* el;
+
+  EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion);
+  el = xml::FindRootElement(versioned_docs[0].get());
+  ASSERT_NE(nullptr, el);
+  EXPECT_EQ(2u, el->attributes.size());
+  EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
+
+  xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(attr->compiled_attribute);
+
+  attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(attr->compiled_attribute);
+
+  EXPECT_EQ(static_cast<uint16_t>(SDK_HONEYCOMB), versioned_docs[1]->file.config.sdkVersion);
+  el = xml::FindRootElement(versioned_docs[1].get());
+  ASSERT_NE(nullptr, el);
+  EXPECT_EQ(1u, el->attributes.size());
+  EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
+  EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingLeft"));
+  EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingRight"));
+
+  attr = el->FindAttribute(xml::kSchemaAndroid, "progressBarPadding");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(attr->compiled_attribute);
+
+  EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[2]->file.config.sdkVersion);
+  el = xml::FindRootElement(versioned_docs[2].get());
+  ASSERT_NE(nullptr, el);
+  EXPECT_EQ(2u, el->attributes.size());
+  EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingLeft"));
+  EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingRight"));
+
+  attr = el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(attr->compiled_attribute);
+
+  attr = el->FindAttribute(xml::kSchemaAndroid, "progressBarPadding");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(attr->compiled_attribute);
+}
+
+TEST_F(XmlCompatVersionerTest, DegradeRuleOverridesExistingAttribute) {
+  auto doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
+      <View xmlns:android="http://schemas.android.com/apk/res/android"
+          android:paddingHorizontal="24dp"
+          android:paddingLeft="16dp"
+          android:paddingRight="16dp"/>)EOF");
+
+  XmlReferenceLinker linker;
+  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
+
+  Item* padding_horizontal_value = xml::FindRootElement(doc.get())
+                                       ->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal")
+                                       ->compiled_value.get();
+  ASSERT_NE(nullptr, padding_horizontal_value);
+
+  XmlCompatVersioner::Rules rules;
+  rules[R::attr::paddingHorizontal] =
+      util::make_unique<DegradeToManyRule>(std::vector<ReplacementAttr>(
+          {ReplacementAttr{"paddingLeft", R::attr::paddingLeft, Attribute(false, TYPE_DIMENSION)},
+           ReplacementAttr{"paddingRight", R::attr::paddingRight,
+                           Attribute(false, TYPE_DIMENSION)}}));
+
+  const util::Range<ApiVersion> api_range{SDK_GINGERBREAD, SDK_O + 1};
+
+  XmlCompatVersioner versioner(&rules);
+  std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
+      versioner.Process(context_.get(), doc.get(), api_range);
+  ASSERT_EQ(2u, versioned_docs.size());
+
+  xml::Element* el;
+
+  EXPECT_EQ(static_cast<uint16_t>(0), versioned_docs[0]->file.config.sdkVersion);
+  el = xml::FindRootElement(versioned_docs[0].get());
+  ASSERT_NE(nullptr, el);
+  EXPECT_EQ(2u, el->attributes.size());
+  EXPECT_EQ(nullptr, el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal"));
+
+  xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
+  ASSERT_TRUE(attr->compiled_attribute);
+
+  attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
+  ASSERT_TRUE(attr->compiled_attribute);
+
+  EXPECT_EQ(static_cast<uint16_t>(SDK_LOLLIPOP_MR1), versioned_docs[1]->file.config.sdkVersion);
+  el = xml::FindRootElement(versioned_docs[1].get());
+  ASSERT_NE(nullptr, el);
+  EXPECT_EQ(3u, el->attributes.size());
+
+  attr = el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
+  ASSERT_TRUE(attr->compiled_attribute);
+
+  attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
+  ASSERT_TRUE(attr->compiled_attribute);
+
+  attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
+  ASSERT_NE(nullptr, attr);
+  ASSERT_NE(nullptr, attr->compiled_value);
+  ASSERT_TRUE(padding_horizontal_value->Equals(attr->compiled_value.get()));
+  ASSERT_TRUE(attr->compiled_attribute);
+}
+
+}  // namespace aapt
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index 94bdccd..721fc26 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -72,13 +72,13 @@
   using xml::PackageAwareVisitor::Visit;
 
   XmlVisitor(const Source& source, const CallSite& callsite, IAaptContext* context,
-             SymbolTable* symbols, std::set<int>* sdk_levels_found)
+             SymbolTable* symbols)
       : source_(source),
         callsite_(callsite),
         context_(context),
         symbols_(symbols),
-        sdk_levels_found_(sdk_levels_found),
-        reference_visitor_(callsite, context, symbols, this) {}
+        reference_visitor_(callsite, context, symbols, this) {
+  }
 
   void Visit(xml::Element* el) override {
     // The default Attribute allows everything except enums or flags.
@@ -118,22 +118,12 @@
           continue;
         }
 
-        // Find this compiled attribute's SDK level.
-        const xml::AaptAttribute& aapt_attr = attr.compiled_attribute.value();
-        if (aapt_attr.id) {
-          // Record all SDK levels from which the attributes were defined.
-          const size_t sdk_level = FindAttributeSdkLevel(aapt_attr.id.value());
-          if (sdk_level > 1) {
-            sdk_levels_found_->insert(sdk_level);
-          }
-        }
-        attribute = &aapt_attr.attribute;
+        attribute = &attr.compiled_attribute.value().attribute;
       }
 
       attr.compiled_value = ResourceUtils::TryParseItemForAttribute(attr.value, attribute);
       if (attr.compiled_value) {
-        // With a compiledValue, we must resolve the reference and assign it an
-        // ID.
+        // With a compiledValue, we must resolve the reference and assign it an ID.
         attr.compiled_value->SetSource(source);
         attr.compiled_value->Accept(&reference_visitor_);
       } else if ((attribute->type_mask & android::ResTable_map::TYPE_STRING) == 0) {
@@ -164,7 +154,6 @@
   IAaptContext* context_;
   SymbolTable* symbols_;
 
-  std::set<int>* sdk_levels_found_;
   ReferenceVisitor reference_visitor_;
   bool error_ = false;
 };
@@ -172,10 +161,8 @@
 }  // namespace
 
 bool XmlReferenceLinker::Consume(IAaptContext* context, xml::XmlResource* resource) {
-  sdk_levels_found_.clear();
   const CallSite callsite = {resource->file.name};
-  XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols(),
-                     &sdk_levels_found_);
+  XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols());
   if (resource->root) {
     resource->root->Accept(&visitor);
     return !visitor.HasError();
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index 66ecc15..de81e73 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -160,16 +160,6 @@
   ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
 }
 
-TEST_F(XmlReferenceLinkerTest, SdkLevelsAreRecorded) {
-  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
-        <View xmlns:android="http://schemas.android.com/apk/res/android"
-              android:colorAccent="#ffffff" />)EOF");
-
-  XmlReferenceLinker linker;
-  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
-  EXPECT_TRUE(linker.sdk_levels().count(21) == 1);
-}
-
 TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
   std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
             <View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 1a648bf..882a85b 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -237,14 +237,12 @@
   }
 
   // We found a resource.
-  std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>();
-  s->id = id;
+  std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>(id);
 
   // Check to see if it is an attribute.
   for (size_t i = 0; i < (size_t)count; i++) {
     if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
-      s->attribute = std::make_shared<Attribute>(false);
-      s->attribute->type_mask = entry[i].map.value.data;
+      s->attribute = std::make_shared<Attribute>(false, entry[i].map.value.data);
       break;
     }
   }
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index bd252d2..4a2181e 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -53,16 +53,12 @@
 class SymbolTable {
  public:
   struct Symbol {
-    Symbol() : Symbol(Maybe<ResourceId>{}) {}
+    Symbol() = default;
 
-    explicit Symbol(const Maybe<ResourceId>& i) : Symbol(i, nullptr) {}
-
-    Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr)
-        : Symbol(i, attr, false) {}
-
-    Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr,
-           bool pub)
-        : id(i), attribute(attr), is_public(pub) {}
+    explicit Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr = {},
+                    bool pub = false)
+        : id(i), attribute(attr), is_public(pub) {
+    }
 
     Symbol(const Symbol&) = default;
     Symbol(Symbol&&) = default;
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 e92565f..0290e30 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -1,5 +1,16 @@
 # 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
+  specific newer attributes to backwards compatible versions of them.
+  Ex: `android:paddingHorizontal` degrades to `android:paddingLeft` and `android:paddingRight`.
+
 ## Version 2.15
 ### `aapt2 compile ...`
 - Add `--no-crunch` option to avoid processing PNGs during the compile phase. Note that this
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/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 30b9af6..386f74b 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -44,6 +44,12 @@
 namespace aapt {
 namespace util {
 
+template <typename T>
+struct Range {
+  T start;
+  T end;
+};
+
 std::vector<std::string> Split(const android::StringPiece& str, char sep);
 std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep);
 
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 98f5f1d..885ab3e 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -356,17 +356,16 @@
   return util::make_unique<XmlResource>(ResourceFile{}, std::move(string_pool), std::move(root));
 }
 
-std::unique_ptr<Node> Namespace::Clone() {
+std::unique_ptr<Node> Namespace::Clone(const ElementCloneFunc& el_cloner) {
   auto ns = util::make_unique<Namespace>();
   ns->comment = comment;
   ns->line_number = line_number;
   ns->column_number = column_number;
   ns->namespace_prefix = namespace_prefix;
   ns->namespace_uri = namespace_uri;
-
   ns->children.reserve(children.size());
   for (const std::unique_ptr<xml::Node>& child : children) {
-    ns->AppendChild(child->Clone());
+    ns->AppendChild(child->Clone(el_cloner));
   }
   return std::move(ns);
 }
@@ -412,6 +411,15 @@
   return nullptr;
 }
 
+const Attribute* Element::FindAttribute(const StringPiece& ns, const StringPiece& name) const {
+  for (const auto& attr : attributes) {
+    if (ns == attr.namespace_uri && name == attr.name) {
+      return &attr;
+    }
+  }
+  return nullptr;
+}
+
 Element* Element::FindChild(const StringPiece& ns, const StringPiece& name) {
   return FindChildWithAttribute(ns, name, {}, {}, {});
 }
@@ -464,29 +472,23 @@
   return elements;
 }
 
-std::unique_ptr<Node> Element::Clone() {
+std::unique_ptr<Node> Element::Clone(const ElementCloneFunc& el_cloner) {
   auto el = util::make_unique<Element>();
   el->comment = comment;
   el->line_number = line_number;
   el->column_number = column_number;
   el->name = name;
   el->namespace_uri = namespace_uri;
-
   el->attributes.reserve(attributes.size());
-  for (xml::Attribute& attr : attributes) {
-    // Don't copy compiled values or attributes.
-    el->attributes.push_back(
-        xml::Attribute{attr.namespace_uri, attr.name, attr.value});
-  }
-
+  el_cloner(*this, el.get());
   el->children.reserve(children.size());
   for (const std::unique_ptr<xml::Node>& child : children) {
-    el->AppendChild(child->Clone());
+    el->AppendChild(child->Clone(el_cloner));
   }
   return std::move(el);
 }
 
-std::unique_ptr<Node> Text::Clone() {
+std::unique_ptr<Node> Text::Clone(const ElementCloneFunc&) {
   auto t = util::make_unique<Text>();
   t->comment = comment;
   t->line_number = line_number;
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index 6950c30..2dc99d6 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -35,6 +35,8 @@
 
 class RawVisitor;
 
+class Element;
+
 /**
  * Base class for all XML nodes.
  */
@@ -51,7 +53,11 @@
   void AppendChild(std::unique_ptr<Node> child);
   void InsertChild(size_t index, std::unique_ptr<Node> child);
   virtual void Accept(RawVisitor* visitor) = 0;
-  virtual std::unique_ptr<Node> Clone() = 0;
+
+  using ElementCloneFunc = std::function<void(const Element&, Element*)>;
+
+  // Clones the Node subtree, using the given function to decide how to clone an Element.
+  virtual std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) = 0;
 };
 
 /**
@@ -72,12 +78,16 @@
   std::string namespace_prefix;
   std::string namespace_uri;
 
-  std::unique_ptr<Node> Clone() override;
+  std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) override;
 };
 
 struct AaptAttribute {
-  Maybe<ResourceId> id;
+  explicit AaptAttribute(const ::aapt::Attribute& attr, const Maybe<ResourceId>& resid = {})
+      : attribute(attr), id(resid) {
+  }
+
   aapt::Attribute attribute;
+  Maybe<ResourceId> id;
 };
 
 /**
@@ -102,6 +112,8 @@
   std::vector<Attribute> attributes;
 
   Attribute* FindAttribute(const android::StringPiece& ns, const android::StringPiece& name);
+  const Attribute* FindAttribute(const android::StringPiece& ns,
+                                 const android::StringPiece& name) const;
   xml::Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name);
   xml::Element* FindChildWithAttribute(const android::StringPiece& ns,
                                        const android::StringPiece& name,
@@ -109,7 +121,7 @@
                                        const android::StringPiece& attr_name,
                                        const android::StringPiece& attr_value);
   std::vector<xml::Element*> GetChildElements();
-  std::unique_ptr<Node> Clone() override;
+  std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) override;
 };
 
 /**
@@ -119,7 +131,7 @@
  public:
   std::string text;
 
-  std::unique_ptr<Node> Clone() override;
+  std::unique_ptr<Node> Clone(const ElementCloneFunc& el_cloner) override;
 };
 
 /**
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());
+    }
 }