Merge "Persist the debug time correction flag."
diff --git a/Android.mk b/Android.mk
index f544620..f318a5a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -265,6 +265,7 @@
 	core/java/android/security/IKeystoreService.aidl \
 	core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl \
 	core/java/android/service/autofill/IAutoFillService.aidl \
+	core/java/android/service/autofill/IAutoFillServiceConnection.aidl \
 	core/java/android/service/autofill/IFillCallback.aidl \
 	core/java/android/service/autofill/ISaveCallback.aidl \
 	core/java/android/service/carrier/ICarrierService.aidl \
diff --git a/api/current.txt b/api/current.txt
index c7074cc..693d512 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -298,6 +298,7 @@
     field public static final int authorities = 16842776; // 0x1010018
     field public static final int autoAdvanceViewId = 16843535; // 0x101030f
     field public static final int autoCompleteTextViewStyle = 16842859; // 0x101006b
+    field public static final int autoFillHint = 16844121; // 0x1010559
     field public static final int autoFillMode = 16844116; // 0x1010554
     field public static final int autoLink = 16842928; // 0x10100b0
     field public static final int autoMirrored = 16843754; // 0x10103ea
@@ -1005,7 +1006,7 @@
     field public static final int preferenceStyle = 16842894; // 0x101008e
     field public static final int presentationTheme = 16843712; // 0x10103c0
     field public static final int previewImage = 16843482; // 0x10102da
-    field public static final int primaryContentAlpha = 16843367; // 0x1010267
+    field public static final int primaryContentAlpha = 16844117; // 0x1010555
     field public static final int priority = 16842780; // 0x101001c
     field public static final int privateImeOptions = 16843299; // 0x1010223
     field public static final int process = 16842769; // 0x1010011
@@ -1061,7 +1062,9 @@
     field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
     field public static final int required = 16843406; // 0x101028e
     field public static final int requiredAccountType = 16843734; // 0x10103d6
+    field public static final int requiredFeature = 16844119; // 0x1010557
     field public static final int requiredForAllUsers = 16843728; // 0x10103d0
+    field public static final int requiredNotFeature = 16844120; // 0x1010558
     field public static final int requiresFadingEdge = 16843685; // 0x10103a5
     field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
     field public static final int resizeClip = 16843983; // 0x10104cf
@@ -1130,7 +1133,7 @@
     field public static final int searchSuggestSelection = 16843224; // 0x10101d8
     field public static final int searchSuggestThreshold = 16843373; // 0x101026d
     field public static final int searchViewStyle = 16843904; // 0x1010480
-    field public static final int secondaryContentAlpha = 16843688; // 0x10103a8
+    field public static final int secondaryContentAlpha = 16844118; // 0x1010556
     field public static final int secondaryProgress = 16843064; // 0x1010138
     field public static final int secondaryProgressTint = 16843879; // 0x1010467
     field public static final int secondaryProgressTintMode = 16843880; // 0x1010468
@@ -3909,8 +3912,9 @@
     method public void readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.ActivityManager.RunningAppProcessInfo> CREATOR;
-    field public static final int IMPORTANCE_BACKGROUND = 400; // 0x190
-    field public static final int IMPORTANCE_EMPTY = 500; // 0x1f4
+    field public static final deprecated int IMPORTANCE_BACKGROUND = 400; // 0x190
+    field public static final int IMPORTANCE_CACHED = 400; // 0x190
+    field public static final deprecated int IMPORTANCE_EMPTY = 500; // 0x1f4
     field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
     field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
     field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
@@ -5482,6 +5486,7 @@
 
   public final class NotificationChannel implements android.os.Parcelable {
     ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
+    ctor public NotificationChannel(java.lang.String, int, int);
     ctor protected NotificationChannel(android.os.Parcel);
     method public boolean canBypassDnd();
     method public boolean canShowBadge();
@@ -5495,6 +5500,7 @@
     method public int getLightColor();
     method public int getLockscreenVisibility();
     method public java.lang.CharSequence getName();
+    method public int getNameResId();
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
     method public void setBypassDnd(boolean);
@@ -5514,12 +5520,14 @@
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
     ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
+    ctor public NotificationChannelGroup(java.lang.String, int);
     ctor protected NotificationChannelGroup(android.os.Parcel);
     method public android.app.NotificationChannelGroup clone();
     method public int describeContents();
     method public java.util.List<android.app.NotificationChannel> getChannels();
     method public java.lang.String getId();
     method public java.lang.CharSequence getName();
+    method public int getNameResId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
   }
@@ -6549,6 +6557,7 @@
 
   public static class AssistStructure.ViewNode {
     method public float getAlpha();
+    method public int getAutoFillHint();
     method public android.view.autofill.AutoFillId getAutoFillId();
     method public java.lang.String[] getAutoFillOptions();
     method public android.view.autofill.AutoFillType getAutoFillType();
@@ -6565,6 +6574,7 @@
     method public java.lang.String getIdEntry();
     method public java.lang.String getIdPackage();
     method public java.lang.String getIdType();
+    method public int getInputType();
     method public int getLeft();
     method public int getScrollX();
     method public int getScrollY();
@@ -8445,8 +8455,10 @@
     field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
     field public static final java.lang.String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
     field public static final java.lang.String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
+    field public static final java.lang.String EXTRA_HONORED_ARGS = "android.content.extra.HONORED_ARGS";
     field public static final java.lang.String EXTRA_REFRESH_SUPPORTED = "android.content.extra.REFRESH_SUPPORTED";
     field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
+    field public static final java.lang.String EXTRA_TOTAL_SIZE = "android.content.extra.TOTAL_SIZE";
     field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
     field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
     field public static final java.lang.String QUERY_ARG_LIMIT = "android:query-page-limit";
@@ -8457,7 +8469,6 @@
     field public static final java.lang.String QUERY_ARG_SQL_SELECTION = "android:query-sql-selection";
     field public static final java.lang.String QUERY_ARG_SQL_SELECTION_ARGS = "android:query-sql-selection-args";
     field public static final java.lang.String QUERY_ARG_SQL_SORT_ORDER = "android:query-sql-sort-order";
-    field public static final java.lang.String QUERY_RESULT_SIZE = "android:query-result-size";
     field public static final int QUERY_SORT_DIRECTION_ASCENDING = 0; // 0x0
     field public static final int QUERY_SORT_DIRECTION_DESCENDING = 1; // 0x1
     field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
@@ -10345,6 +10356,7 @@
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean hasSystemFeature(java.lang.String, int);
     method public abstract boolean isInstantApp();
+    method public abstract boolean isInstantApp(java.lang.String);
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -10476,8 +10488,11 @@
     field public static final int GET_SIGNATURES = 64; // 0x40
     field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
+    field public static final int INSTALL_REASON_DEVICE_RESTORE = 2; // 0x2
+    field public static final int INSTALL_REASON_DEVICE_SETUP = 3; // 0x3
     field public static final int INSTALL_REASON_POLICY = 1; // 0x1
     field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
+    field public static final int INSTALL_REASON_USER = 4; // 0x4
     field public static final int MATCH_ALL = 131072; // 0x20000
     field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
     field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
@@ -13829,12 +13844,12 @@
   }
 
   public final class Icon implements android.os.Parcelable {
+    method public static android.graphics.drawable.Icon createWithAdaptiveBitmap(android.graphics.Bitmap);
     method public static android.graphics.drawable.Icon createWithBitmap(android.graphics.Bitmap);
     method public static android.graphics.drawable.Icon createWithContentUri(java.lang.String);
     method public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri);
     method public static android.graphics.drawable.Icon createWithData(byte[], int, int);
     method public static android.graphics.drawable.Icon createWithFilePath(java.lang.String);
-    method public static android.graphics.drawable.Icon createWithMaskableBitmap(android.graphics.Bitmap);
     method public static android.graphics.drawable.Icon createWithResource(android.content.Context, int);
     method public static android.graphics.drawable.Icon createWithResource(java.lang.String, int);
     method public int describeContents();
@@ -30125,12 +30140,12 @@
     field public static final int BATTERY_PLUGGED_AC = 1; // 0x1
     field public static final int BATTERY_PLUGGED_USB = 2; // 0x2
     field public static final int BATTERY_PLUGGED_WIRELESS = 4; // 0x4
-    field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6
     field public static final int BATTERY_PROPERTY_CAPACITY = 4; // 0x4
     field public static final int BATTERY_PROPERTY_CHARGE_COUNTER = 1; // 0x1
     field public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3; // 0x3
     field public static final int BATTERY_PROPERTY_CURRENT_NOW = 2; // 0x2
     field public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5; // 0x5
+    field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6
     field public static final int BATTERY_STATUS_CHARGING = 2; // 0x2
     field public static final int BATTERY_STATUS_DISCHARGING = 3; // 0x3
     field public static final int BATTERY_STATUS_FULL = 5; // 0x5
@@ -36411,7 +36426,6 @@
     field public static final int PURPOSE_ENCRYPT = 1; // 0x1
     field public static final int PURPOSE_SIGN = 4; // 0x4
     field public static final int PURPOSE_VERIFY = 8; // 0x8
-    field public static final int PURPOSE_WRAP_KEY = 16; // 0x10
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PSS = "PSS";
   }
@@ -36463,6 +36477,7 @@
 
   public abstract class AutoFillService extends android.app.Service {
     ctor public AutoFillService();
+    method public final void disableSelf();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
@@ -36525,6 +36540,7 @@
     method public android.service.autofill.SaveInfo.Builder addSavableIds(android.view.autofill.AutoFillId...);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
+    method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender);
   }
 
 }
@@ -36922,6 +36938,7 @@
     method public final void unlockAndRun(java.lang.Runnable);
     field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
     field public static final java.lang.String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
+    field public static final java.lang.String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT";
     field public static final java.lang.String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
   }
 
@@ -38231,6 +38248,7 @@
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
+    field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
     field public static final int PROPERTY_WIFI = 8; // 0x8
   }
 
@@ -38800,6 +38818,7 @@
     method public boolean handleMmi(java.lang.String);
     method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
     method public boolean isInCall();
+    method public boolean isInManagedCall();
     method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
     method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
     method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
@@ -38836,6 +38855,7 @@
     field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
     field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
     field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
+    field public static final java.lang.String METADATA_INCLUDE_SELF_MANAGED_CALLS = "android.telecom.INCLUDE_SELF_MANAGED_CALLS";
     field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
     field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
     field public static final int PRESENTATION_ALLOWED = 1; // 0x1
@@ -39523,6 +39543,7 @@
     method public java.lang.String getDeviceId();
     method public java.lang.String getDeviceId(int);
     method public java.lang.String getDeviceSoftwareVersion();
+    method public java.lang.String[] getForbiddenPlmns();
     method public java.lang.String getGroupIdLevel1();
     method public java.lang.String getIccAuthentication(int, int, java.lang.String);
     method public java.lang.String getLine1Number();
@@ -40371,6 +40392,7 @@
     method public boolean hasSystemFeature(java.lang.String);
     method public boolean hasSystemFeature(java.lang.String, int);
     method public boolean isInstantApp();
+    method public boolean isInstantApp(java.lang.String);
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -44805,6 +44827,7 @@
     method public float getAlpha();
     method public android.view.animation.Animation getAnimation();
     method public android.os.IBinder getApplicationWindowToken();
+    method public int getAutoFillHint();
     method public int getAutoFillMode();
     method public android.view.autofill.AutoFillType getAutoFillType();
     method public android.view.autofill.AutoFillValue getAutoFillValue();
@@ -45122,6 +45145,7 @@
     method public void setActivated(boolean);
     method public void setAlpha(float);
     method public void setAnimation(android.view.animation.Animation);
+    method public void setAutoFillHint(int);
     method public void setAutoFillMode(int);
     method public void setBackground(android.graphics.drawable.Drawable);
     method public void setBackgroundColor(int);
@@ -45263,6 +45287,20 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 512; // 0x200
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 4096; // 0x1000
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 1024; // 0x400
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = 2048; // 0x800
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_NUMBER = 128; // 0x80
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_SECURITY_CODE = 256; // 0x100
+    field public static final int AUTO_FILL_HINT_EMAIL_ADDRESS = 1; // 0x1
+    field public static final int AUTO_FILL_HINT_NAME = 2; // 0x2
+    field public static final int AUTO_FILL_HINT_NONE = 0; // 0x0
+    field public static final int AUTO_FILL_HINT_PASSWORD = 8; // 0x8
+    field public static final int AUTO_FILL_HINT_PHONE = 16; // 0x10
+    field public static final int AUTO_FILL_HINT_POSTAL_ADDRESS = 32; // 0x20
+    field public static final int AUTO_FILL_HINT_POSTAL_CODE = 64; // 0x40
+    field public static final int AUTO_FILL_HINT_USERNAME = 4; // 0x4
     field public static final int AUTO_FILL_MODE_AUTO = 1; // 0x1
     field public static final int AUTO_FILL_MODE_INHERIT = 0; // 0x0
     field public static final int AUTO_FILL_MODE_MANUAL = 2; // 0x2
@@ -45915,6 +45953,7 @@
     method public abstract void setAccessibilityFocused(boolean);
     method public abstract void setActivated(boolean);
     method public abstract void setAlpha(float);
+    method public abstract void setAutoFillHint(int);
     method public abstract void setAutoFillOptions(java.lang.String[]);
     method public abstract void setAutoFillType(android.view.autofill.AutoFillType);
     method public abstract void setAutoFillValue(android.view.autofill.AutoFillValue);
@@ -45932,6 +45971,7 @@
     method public abstract void setFocused(boolean);
     method public abstract void setHint(java.lang.CharSequence);
     method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String);
+    method public abstract void setInputType(int);
     method public abstract void setLongClickable(boolean);
     method public abstract void setSanitized(boolean);
     method public abstract void setSelected(boolean);
@@ -47183,10 +47223,11 @@
 
   public final class AutoFillType implements android.os.Parcelable {
     method public int describeContents();
+    method public static android.view.autofill.AutoFillType forDate();
     method public static android.view.autofill.AutoFillType forList();
-    method public static android.view.autofill.AutoFillType forText(int);
+    method public static android.view.autofill.AutoFillType forText();
     method public static android.view.autofill.AutoFillType forToggle();
-    method public int getSubType();
+    method public boolean isDate();
     method public boolean isList();
     method public boolean isText();
     method public boolean isToggle();
@@ -47196,9 +47237,11 @@
 
   public final class AutoFillValue implements android.os.Parcelable {
     method public int describeContents();
+    method public static android.view.autofill.AutoFillValue forDate(long);
     method public static android.view.autofill.AutoFillValue forList(int);
     method public static android.view.autofill.AutoFillValue forText(java.lang.CharSequence);
     method public static android.view.autofill.AutoFillValue forToggle(boolean);
+    method public long getDateValue();
     method public int getListValue();
     method public java.lang.CharSequence getTextValue();
     method public boolean getToggleValue();
diff --git a/api/system-current.txt b/api/system-current.txt
index a467351..f095936 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -207,7 +207,6 @@
     field public static final java.lang.String REQUEST_DELETE_PACKAGES = "android.permission.REQUEST_DELETE_PACKAGES";
     field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
     field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES";
-    field public static final java.lang.String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
     field public static final java.lang.String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
     field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
@@ -411,6 +410,7 @@
     field public static final int authorities = 16842776; // 0x1010018
     field public static final int autoAdvanceViewId = 16843535; // 0x101030f
     field public static final int autoCompleteTextViewStyle = 16842859; // 0x101006b
+    field public static final int autoFillHint = 16844121; // 0x1010559
     field public static final int autoFillMode = 16844116; // 0x1010554
     field public static final int autoLink = 16842928; // 0x10100b0
     field public static final int autoMirrored = 16843754; // 0x10103ea
@@ -1118,7 +1118,7 @@
     field public static final int preferenceStyle = 16842894; // 0x101008e
     field public static final int presentationTheme = 16843712; // 0x10103c0
     field public static final int previewImage = 16843482; // 0x10102da
-    field public static final int primaryContentAlpha = 16843367; // 0x1010267
+    field public static final int primaryContentAlpha = 16844117; // 0x1010555
     field public static final int priority = 16842780; // 0x101001c
     field public static final int privateImeOptions = 16843299; // 0x1010223
     field public static final int process = 16842769; // 0x1010011
@@ -1174,7 +1174,9 @@
     field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
     field public static final int required = 16843406; // 0x101028e
     field public static final int requiredAccountType = 16843734; // 0x10103d6
+    field public static final int requiredFeature = 16844119; // 0x1010557
     field public static final int requiredForAllUsers = 16843728; // 0x10103d0
+    field public static final int requiredNotFeature = 16844120; // 0x1010558
     field public static final int requiresFadingEdge = 16843685; // 0x10103a5
     field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
     field public static final int resizeClip = 16843983; // 0x10104cf
@@ -1247,7 +1249,7 @@
     field public static final int searchSuggestSelection = 16843224; // 0x10101d8
     field public static final int searchSuggestThreshold = 16843373; // 0x101026d
     field public static final int searchViewStyle = 16843904; // 0x1010480
-    field public static final int secondaryContentAlpha = 16843688; // 0x10103a8
+    field public static final int secondaryContentAlpha = 16844118; // 0x1010556
     field public static final int secondaryProgress = 16843064; // 0x1010138
     field public static final int secondaryProgressTint = 16843879; // 0x1010467
     field public static final int secondaryProgressTintMode = 16843880; // 0x1010468
@@ -4048,8 +4050,9 @@
     method public void readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.ActivityManager.RunningAppProcessInfo> CREATOR;
-    field public static final int IMPORTANCE_BACKGROUND = 400; // 0x190
-    field public static final int IMPORTANCE_EMPTY = 500; // 0x1f4
+    field public static final deprecated int IMPORTANCE_BACKGROUND = 400; // 0x190
+    field public static final int IMPORTANCE_CACHED = 400; // 0x190
+    field public static final deprecated int IMPORTANCE_EMPTY = 500; // 0x1f4
     field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
     field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
     field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
@@ -5659,6 +5662,7 @@
 
   public final class NotificationChannel implements android.os.Parcelable {
     ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
+    ctor public NotificationChannel(java.lang.String, int, int);
     ctor protected NotificationChannel(android.os.Parcel);
     method public boolean canBypassDnd();
     method public boolean canShowBadge();
@@ -5672,6 +5676,7 @@
     method public int getLightColor();
     method public int getLockscreenVisibility();
     method public java.lang.CharSequence getName();
+    method public int getNameResId();
     method public android.net.Uri getSound();
     method public int getUserLockedFields();
     method public long[] getVibrationPattern();
@@ -5708,6 +5713,7 @@
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
     ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
+    ctor public NotificationChannelGroup(java.lang.String, int);
     ctor protected NotificationChannelGroup(android.os.Parcel);
     method public void addChannel(android.app.NotificationChannel);
     method public android.app.NotificationChannelGroup clone();
@@ -5715,6 +5721,7 @@
     method public java.util.List<android.app.NotificationChannel> getChannels();
     method public java.lang.String getId();
     method public java.lang.CharSequence getName();
+    method public int getNameResId();
     method public org.json.JSONObject toJson() throws org.json.JSONException;
     method public void writeToParcel(android.os.Parcel, int);
     method public void writeXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
@@ -6789,6 +6796,7 @@
 
   public static class AssistStructure.ViewNode {
     method public float getAlpha();
+    method public int getAutoFillHint();
     method public android.view.autofill.AutoFillId getAutoFillId();
     method public java.lang.String[] getAutoFillOptions();
     method public android.view.autofill.AutoFillType getAutoFillType();
@@ -6805,6 +6813,7 @@
     method public java.lang.String getIdEntry();
     method public java.lang.String getIdPackage();
     method public java.lang.String getIdType();
+    method public int getInputType();
     method public int getLeft();
     method public int getScrollX();
     method public int getScrollY();
@@ -6942,26 +6951,64 @@
   public class BackupManagerMonitor {
     ctor public BackupManagerMonitor();
     method public void onEvent(android.os.Bundle);
+    field public static final java.lang.String EXTRA_LOG_CANCEL_ALL = "android.app.backup.extra.LOG_CANCEL_ALL";
     field public static final java.lang.String EXTRA_LOG_EVENT_CATEGORY = "android.app.backup.extra.LOG_EVENT_CATEGORY";
     field public static final java.lang.String EXTRA_LOG_EVENT_ID = "android.app.backup.extra.LOG_EVENT_ID";
     field public static final java.lang.String EXTRA_LOG_EVENT_PACKAGE_NAME = "android.app.backup.extra.LOG_EVENT_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_LOG_EVENT_PACKAGE_VERSION = "android.app.backup.extra.LOG_EVENT_PACKAGE_VERSION";
+    field public static final java.lang.String EXTRA_LOG_EXCEPTION_FULL_BACKUP = "android.app.backup.extra.LOG_EXCEPTION_FULL_BACKUP";
+    field public static final java.lang.String EXTRA_LOG_ILLEGAL_KEY = "android.app.backup.extra.LOG_ILLEGAL_KEY";
+    field public static final java.lang.String EXTRA_LOG_MANIFEST_PACKAGE_NAME = "android.app.backup.extra.LOG_MANIFEST_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_LOG_OLD_VERSION = "android.app.backup.extra.LOG_OLD_VERSION";
+    field public static final java.lang.String EXTRA_LOG_POLICY_ALLOW_APKS = "android.app.backup.extra.LOG_POLICY_ALLOW_APKS";
+    field public static final java.lang.String EXTRA_LOG_PREFLIGHT_ERROR = "android.app.backup.extra.LOG_PREFLIGHT_ERROR";
+    field public static final java.lang.String EXTRA_LOG_RESTORE_ANYWAY = "android.app.backup.extra.LOG_RESTORE_ANYWAY";
+    field public static final java.lang.String EXTRA_LOG_RESTORE_VERSION = "android.app.backup.extra.LOG_RESTORE_VERSION";
+    field public static final java.lang.String EXTRA_LOG_WIDGET_PACKAGE_NAME = "android.app.backup.extra.LOG_WIDGET_PACKAGE_NAME";
     field public static final int LOG_EVENT_CATEGORY_AGENT = 2; // 0x2
     field public static final int LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY = 3; // 0x3
     field public static final int LOG_EVENT_CATEGORY_TRANSPORT = 1; // 0x1
+    field public static final int LOG_EVENT_ID_APK_NOT_INSTALLED = 40; // 0x28
     field public static final int LOG_EVENT_ID_APP_HAS_NO_AGENT = 28; // 0x1c
+    field public static final int LOG_EVENT_ID_BACKUP_DISABLED = 13; // 0xd
+    field public static final int LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK = 41; // 0x29
     field public static final int LOG_EVENT_ID_CANT_FIND_AGENT = 30; // 0x1e
-    field public static final int LOG_EVENT_ID_FULL_BACKUP_TIMEOUT = 4; // 0x4
+    field public static final int LOG_EVENT_ID_CORRUPT_MANIFEST = 46; // 0x2e
+    field public static final int LOG_EVENT_ID_DEVICE_NOT_PROVISIONED = 14; // 0xe
+    field public static final int LOG_EVENT_ID_ERROR_PREFLIGHT = 16; // 0x10
+    field public static final int LOG_EVENT_ID_EXCEPTION_FULL_BACKUP = 19; // 0x13
+    field public static final int LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE = 43; // 0x2b
+    field public static final int LOG_EVENT_ID_FULL_BACKUP_CANCEL = 4; // 0x4
+    field public static final int LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE = 39; // 0x27
+    field public static final int LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH = 37; // 0x25
     field public static final int LOG_EVENT_ID_FULL_RESTORE_TIMEOUT = 45; // 0x2d
-    field public static final int LOG_EVENT_ID_KEY_VALUE_BACKUP_TIMEOUT = 21; // 0x15
+    field public static final int LOG_EVENT_ID_ILLEGAL_KEY = 5; // 0x5
+    field public static final int LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL = 21; // 0x15
     field public static final int LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT = 31; // 0x1f
+    field public static final int LOG_EVENT_ID_LOST_TRANSPORT = 25; // 0x19
+    field public static final int LOG_EVENT_ID_MISSING_SIGNATURE = 42; // 0x2a
+    field public static final int LOG_EVENT_ID_NO_DATA_TO_SEND = 7; // 0x7
     field public static final int LOG_EVENT_ID_NO_PACKAGES = 49; // 0x31
+    field public static final int LOG_EVENT_ID_NO_PM_METADATA_RECEIVED = 23; // 0x17
     field public static final int LOG_EVENT_ID_NO_RESTORE_METADATA_AVAILABLE = 22; // 0x16
+    field public static final int LOG_EVENT_ID_PACKAGE_INELIGIBLE = 9; // 0x9
+    field public static final int LOG_EVENT_ID_PACKAGE_KEY_VALUE_PARTICIPANT = 10; // 0xa
     field public static final int LOG_EVENT_ID_PACKAGE_NOT_FOUND = 12; // 0xc
     field public static final int LOG_EVENT_ID_PACKAGE_NOT_PRESENT = 26; // 0x1a
+    field public static final int LOG_EVENT_ID_PACKAGE_STOPPED = 11; // 0xb
     field public static final int LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT = 15; // 0xf
+    field public static final int LOG_EVENT_ID_PM_AGENT_HAS_NO_METADATA = 24; // 0x18
+    field public static final int LOG_EVENT_ID_QUOTA_HIT_PREFLIGHT = 18; // 0x12
+    field public static final int LOG_EVENT_ID_RESTORE_ANY_VERSION = 34; // 0x22
+    field public static final int LOG_EVENT_ID_RESTORE_VERSION_HIGHER = 27; // 0x1b
+    field public static final int LOG_EVENT_ID_SIGNATURE_MISMATCH = 29; // 0x1d
+    field public static final int LOG_EVENT_ID_SYSTEM_APP_NO_AGENT = 38; // 0x26
+    field public static final int LOG_EVENT_ID_TRANSPORT_IS_NULL = 50; // 0x32
+    field public static final int LOG_EVENT_ID_UNKNOWN_VERSION = 44; // 0x2c
+    field public static final int LOG_EVENT_ID_VERSIONS_MATCH = 35; // 0x23
     field public static final int LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER = 36; // 0x24
+    field public static final int LOG_EVENT_ID_WIDGET_METADATA_MISMATCH = 47; // 0x2f
+    field public static final int LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION = 48; // 0x30
   }
 
   public abstract class BackupObserver {
@@ -8898,8 +8945,10 @@
     field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
     field public static final java.lang.String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
     field public static final java.lang.String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
+    field public static final java.lang.String EXTRA_HONORED_ARGS = "android.content.extra.HONORED_ARGS";
     field public static final java.lang.String EXTRA_REFRESH_SUPPORTED = "android.content.extra.REFRESH_SUPPORTED";
     field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
+    field public static final java.lang.String EXTRA_TOTAL_SIZE = "android.content.extra.TOTAL_SIZE";
     field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
     field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
     field public static final java.lang.String QUERY_ARG_LIMIT = "android:query-page-limit";
@@ -8910,7 +8959,6 @@
     field public static final java.lang.String QUERY_ARG_SQL_SELECTION = "android:query-sql-selection";
     field public static final java.lang.String QUERY_ARG_SQL_SELECTION_ARGS = "android:query-sql-selection-args";
     field public static final java.lang.String QUERY_ARG_SQL_SORT_ORDER = "android:query-sql-sort-order";
-    field public static final java.lang.String QUERY_RESULT_SIZE = "android:query-result-size";
     field public static final int QUERY_SORT_DIRECTION_ASCENDING = 0; // 0x0
     field public static final int QUERY_SORT_DIRECTION_DESCENDING = 1; // 0x1
     field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
@@ -10895,6 +10943,7 @@
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean hasSystemFeature(java.lang.String, int);
     method public abstract boolean isInstantApp();
+    method public abstract boolean isInstantApp(java.lang.String);
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -11079,8 +11128,11 @@
     field public static final int INSTALL_PARSE_FAILED_NOT_APK = -100; // 0xffffff9c
     field public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103; // 0xffffff99
     field public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102; // 0xffffff9a
+    field public static final int INSTALL_REASON_DEVICE_RESTORE = 2; // 0x2
+    field public static final int INSTALL_REASON_DEVICE_SETUP = 3; // 0x3
     field public static final int INSTALL_REASON_POLICY = 1; // 0x1
     field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
+    field public static final int INSTALL_REASON_USER = 4; // 0x4
     field public static final int INSTALL_SUCCEEDED = 1; // 0x1
     field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2; // 0x2
     field public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK = 4; // 0x4
@@ -14465,12 +14517,12 @@
   }
 
   public final class Icon implements android.os.Parcelable {
+    method public static android.graphics.drawable.Icon createWithAdaptiveBitmap(android.graphics.Bitmap);
     method public static android.graphics.drawable.Icon createWithBitmap(android.graphics.Bitmap);
     method public static android.graphics.drawable.Icon createWithContentUri(java.lang.String);
     method public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri);
     method public static android.graphics.drawable.Icon createWithData(byte[], int, int);
     method public static android.graphics.drawable.Icon createWithFilePath(java.lang.String);
-    method public static android.graphics.drawable.Icon createWithMaskableBitmap(android.graphics.Bitmap);
     method public static android.graphics.drawable.Icon createWithResource(android.content.Context, int);
     method public static android.graphics.drawable.Icon createWithResource(java.lang.String, int);
     method public int describeContents();
@@ -32727,12 +32779,12 @@
     field public static final int BATTERY_PLUGGED_AC = 1; // 0x1
     field public static final int BATTERY_PLUGGED_USB = 2; // 0x2
     field public static final int BATTERY_PLUGGED_WIRELESS = 4; // 0x4
-    field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6
     field public static final int BATTERY_PROPERTY_CAPACITY = 4; // 0x4
     field public static final int BATTERY_PROPERTY_CHARGE_COUNTER = 1; // 0x1
     field public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3; // 0x3
     field public static final int BATTERY_PROPERTY_CURRENT_NOW = 2; // 0x2
     field public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5; // 0x5
+    field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6
     field public static final int BATTERY_STATUS_CHARGING = 2; // 0x2
     field public static final int BATTERY_STATUS_DISCHARGING = 3; // 0x3
     field public static final int BATTERY_STATUS_FULL = 5; // 0x5
@@ -39335,7 +39387,6 @@
     field public static final int PURPOSE_ENCRYPT = 1; // 0x1
     field public static final int PURPOSE_SIGN = 4; // 0x4
     field public static final int PURPOSE_VERIFY = 8; // 0x8
-    field public static final int PURPOSE_WRAP_KEY = 16; // 0x10
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PSS = "PSS";
   }
@@ -39387,6 +39438,7 @@
 
   public abstract class AutoFillService extends android.app.Service {
     ctor public AutoFillService();
+    method public final void disableSelf();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
@@ -39449,6 +39501,7 @@
     method public android.service.autofill.SaveInfo.Builder addSavableIds(android.view.autofill.AutoFillId...);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
+    method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender);
   }
 
 }
@@ -39920,6 +39973,7 @@
     method public final void unlockAndRun(java.lang.Runnable);
     field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
     field public static final java.lang.String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
+    field public static final java.lang.String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT";
     field public static final java.lang.String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
   }
 
@@ -41282,6 +41336,7 @@
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
+    field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
     field public static final int PROPERTY_WIFI = 8; // 0x8
   }
 
@@ -42041,6 +42096,7 @@
     method public boolean handleMmi(java.lang.String);
     method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
     method public boolean isInCall();
+    method public boolean isInManagedCall();
     method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
     method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
     method public boolean isRinging();
@@ -42084,6 +42140,7 @@
     field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
     field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
     field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
+    field public static final java.lang.String METADATA_INCLUDE_SELF_MANAGED_CALLS = "android.telecom.INCLUDE_SELF_MANAGED_CALLS";
     field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
     field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
     field public static final int PRESENTATION_ALLOWED = 1; // 0x1
@@ -42814,6 +42871,7 @@
     method public java.lang.String getDeviceId();
     method public java.lang.String getDeviceId(int);
     method public java.lang.String getDeviceSoftwareVersion();
+    method public java.lang.String[] getForbiddenPlmns();
     method public java.lang.String getGroupIdLevel1();
     method public java.lang.String getIccAuthentication(int, int, java.lang.String);
     method public java.lang.String getImei();
@@ -43713,6 +43771,7 @@
     method public boolean hasSystemFeature(java.lang.String);
     method public boolean hasSystemFeature(java.lang.String, int);
     method public boolean isInstantApp();
+    method public boolean isInstantApp(java.lang.String);
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -48154,6 +48213,7 @@
     method public float getAlpha();
     method public android.view.animation.Animation getAnimation();
     method public android.os.IBinder getApplicationWindowToken();
+    method public int getAutoFillHint();
     method public int getAutoFillMode();
     method public android.view.autofill.AutoFillType getAutoFillType();
     method public android.view.autofill.AutoFillValue getAutoFillValue();
@@ -48471,6 +48531,7 @@
     method public void setActivated(boolean);
     method public void setAlpha(float);
     method public void setAnimation(android.view.animation.Animation);
+    method public void setAutoFillHint(int);
     method public void setAutoFillMode(int);
     method public void setBackground(android.graphics.drawable.Drawable);
     method public void setBackgroundColor(int);
@@ -48612,6 +48673,20 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 512; // 0x200
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 4096; // 0x1000
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 1024; // 0x400
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = 2048; // 0x800
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_NUMBER = 128; // 0x80
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_SECURITY_CODE = 256; // 0x100
+    field public static final int AUTO_FILL_HINT_EMAIL_ADDRESS = 1; // 0x1
+    field public static final int AUTO_FILL_HINT_NAME = 2; // 0x2
+    field public static final int AUTO_FILL_HINT_NONE = 0; // 0x0
+    field public static final int AUTO_FILL_HINT_PASSWORD = 8; // 0x8
+    field public static final int AUTO_FILL_HINT_PHONE = 16; // 0x10
+    field public static final int AUTO_FILL_HINT_POSTAL_ADDRESS = 32; // 0x20
+    field public static final int AUTO_FILL_HINT_POSTAL_CODE = 64; // 0x40
+    field public static final int AUTO_FILL_HINT_USERNAME = 4; // 0x4
     field public static final int AUTO_FILL_MODE_AUTO = 1; // 0x1
     field public static final int AUTO_FILL_MODE_INHERIT = 0; // 0x0
     field public static final int AUTO_FILL_MODE_MANUAL = 2; // 0x2
@@ -49264,6 +49339,7 @@
     method public abstract void setAccessibilityFocused(boolean);
     method public abstract void setActivated(boolean);
     method public abstract void setAlpha(float);
+    method public abstract void setAutoFillHint(int);
     method public abstract void setAutoFillOptions(java.lang.String[]);
     method public abstract void setAutoFillType(android.view.autofill.AutoFillType);
     method public abstract void setAutoFillValue(android.view.autofill.AutoFillValue);
@@ -49281,6 +49357,7 @@
     method public abstract void setFocused(boolean);
     method public abstract void setHint(java.lang.CharSequence);
     method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String);
+    method public abstract void setInputType(int);
     method public abstract void setLongClickable(boolean);
     method public abstract void setSanitized(boolean);
     method public abstract void setSelected(boolean);
@@ -50535,10 +50612,11 @@
 
   public final class AutoFillType implements android.os.Parcelable {
     method public int describeContents();
+    method public static android.view.autofill.AutoFillType forDate();
     method public static android.view.autofill.AutoFillType forList();
-    method public static android.view.autofill.AutoFillType forText(int);
+    method public static android.view.autofill.AutoFillType forText();
     method public static android.view.autofill.AutoFillType forToggle();
-    method public int getSubType();
+    method public boolean isDate();
     method public boolean isList();
     method public boolean isText();
     method public boolean isToggle();
@@ -50548,9 +50626,11 @@
 
   public final class AutoFillValue implements android.os.Parcelable {
     method public int describeContents();
+    method public static android.view.autofill.AutoFillValue forDate(long);
     method public static android.view.autofill.AutoFillValue forList(int);
     method public static android.view.autofill.AutoFillValue forText(java.lang.CharSequence);
     method public static android.view.autofill.AutoFillValue forToggle(boolean);
+    method public long getDateValue();
     method public int getListValue();
     method public java.lang.CharSequence getTextValue();
     method public boolean getToggleValue();
diff --git a/api/test-current.txt b/api/test-current.txt
index 1a12b4c..4585b8e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -298,6 +298,7 @@
     field public static final int authorities = 16842776; // 0x1010018
     field public static final int autoAdvanceViewId = 16843535; // 0x101030f
     field public static final int autoCompleteTextViewStyle = 16842859; // 0x101006b
+    field public static final int autoFillHint = 16844121; // 0x1010559
     field public static final int autoFillMode = 16844116; // 0x1010554
     field public static final int autoLink = 16842928; // 0x10100b0
     field public static final int autoMirrored = 16843754; // 0x10103ea
@@ -1005,7 +1006,7 @@
     field public static final int preferenceStyle = 16842894; // 0x101008e
     field public static final int presentationTheme = 16843712; // 0x10103c0
     field public static final int previewImage = 16843482; // 0x10102da
-    field public static final int primaryContentAlpha = 16843367; // 0x1010267
+    field public static final int primaryContentAlpha = 16844117; // 0x1010555
     field public static final int priority = 16842780; // 0x101001c
     field public static final int privateImeOptions = 16843299; // 0x1010223
     field public static final int process = 16842769; // 0x1010011
@@ -1061,7 +1062,9 @@
     field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
     field public static final int required = 16843406; // 0x101028e
     field public static final int requiredAccountType = 16843734; // 0x10103d6
+    field public static final int requiredFeature = 16844119; // 0x1010557
     field public static final int requiredForAllUsers = 16843728; // 0x10103d0
+    field public static final int requiredNotFeature = 16844120; // 0x1010558
     field public static final int requiresFadingEdge = 16843685; // 0x10103a5
     field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
     field public static final int resizeClip = 16843983; // 0x10104cf
@@ -1130,7 +1133,7 @@
     field public static final int searchSuggestSelection = 16843224; // 0x10101d8
     field public static final int searchSuggestThreshold = 16843373; // 0x101026d
     field public static final int searchViewStyle = 16843904; // 0x1010480
-    field public static final int secondaryContentAlpha = 16843688; // 0x10103a8
+    field public static final int secondaryContentAlpha = 16844118; // 0x1010556
     field public static final int secondaryProgress = 16843064; // 0x1010138
     field public static final int secondaryProgressTint = 16843879; // 0x1010467
     field public static final int secondaryProgressTintMode = 16843880; // 0x1010468
@@ -3918,8 +3921,9 @@
     method public void readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.ActivityManager.RunningAppProcessInfo> CREATOR;
-    field public static final int IMPORTANCE_BACKGROUND = 400; // 0x190
-    field public static final int IMPORTANCE_EMPTY = 500; // 0x1f4
+    field public static final deprecated int IMPORTANCE_BACKGROUND = 400; // 0x190
+    field public static final int IMPORTANCE_CACHED = 400; // 0x190
+    field public static final deprecated int IMPORTANCE_EMPTY = 500; // 0x1f4
     field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
     field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
     field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
@@ -5492,6 +5496,7 @@
 
   public final class NotificationChannel implements android.os.Parcelable {
     ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
+    ctor public NotificationChannel(java.lang.String, int, int);
     ctor protected NotificationChannel(android.os.Parcel);
     method public boolean canBypassDnd();
     method public boolean canShowBadge();
@@ -5505,6 +5510,7 @@
     method public int getLightColor();
     method public int getLockscreenVisibility();
     method public java.lang.CharSequence getName();
+    method public int getNameResId();
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
     method public void setBypassDnd(boolean);
@@ -5524,12 +5530,14 @@
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
     ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
+    ctor public NotificationChannelGroup(java.lang.String, int);
     ctor protected NotificationChannelGroup(android.os.Parcel);
     method public android.app.NotificationChannelGroup clone();
     method public int describeContents();
     method public java.util.List<android.app.NotificationChannel> getChannels();
     method public java.lang.String getId();
     method public java.lang.CharSequence getName();
+    method public int getNameResId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
   }
@@ -6259,6 +6267,7 @@
     method public long getMaximumTimeToLock(android.content.ComponentName);
     method public int getOrganizationColor(android.content.ComponentName);
     method public java.lang.CharSequence getOrganizationName(android.content.ComponentName);
+    method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(android.os.UserHandle);
     method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
     method public long getPasswordExpiration(android.content.ComponentName);
     method public long getPasswordExpirationTimeout(android.content.ComponentName);
@@ -6575,6 +6584,7 @@
 
   public static class AssistStructure.ViewNode {
     method public float getAlpha();
+    method public int getAutoFillHint();
     method public android.view.autofill.AutoFillId getAutoFillId();
     method public java.lang.String[] getAutoFillOptions();
     method public android.view.autofill.AutoFillType getAutoFillType();
@@ -6591,6 +6601,7 @@
     method public java.lang.String getIdEntry();
     method public java.lang.String getIdPackage();
     method public java.lang.String getIdType();
+    method public int getInputType();
     method public int getLeft();
     method public int getScrollX();
     method public int getScrollY();
@@ -8472,8 +8483,10 @@
     field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
     field public static final java.lang.String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
     field public static final java.lang.String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
+    field public static final java.lang.String EXTRA_HONORED_ARGS = "android.content.extra.HONORED_ARGS";
     field public static final java.lang.String EXTRA_REFRESH_SUPPORTED = "android.content.extra.REFRESH_SUPPORTED";
     field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
+    field public static final java.lang.String EXTRA_TOTAL_SIZE = "android.content.extra.TOTAL_SIZE";
     field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
     field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
     field public static final java.lang.String QUERY_ARG_LIMIT = "android:query-page-limit";
@@ -8484,7 +8497,6 @@
     field public static final java.lang.String QUERY_ARG_SQL_SELECTION = "android:query-sql-selection";
     field public static final java.lang.String QUERY_ARG_SQL_SELECTION_ARGS = "android:query-sql-selection-args";
     field public static final java.lang.String QUERY_ARG_SQL_SORT_ORDER = "android:query-sql-sort-order";
-    field public static final java.lang.String QUERY_RESULT_SIZE = "android:query-result-size";
     field public static final int QUERY_SORT_DIRECTION_ASCENDING = 0; // 0x0
     field public static final int QUERY_SORT_DIRECTION_DESCENDING = 1; // 0x1
     field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
@@ -10379,6 +10391,7 @@
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean hasSystemFeature(java.lang.String, int);
     method public abstract boolean isInstantApp();
+    method public abstract boolean isInstantApp(java.lang.String);
     method public abstract boolean isPermissionReviewModeEnabled();
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
@@ -10511,8 +10524,11 @@
     field public static final int GET_SIGNATURES = 64; // 0x40
     field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
+    field public static final int INSTALL_REASON_DEVICE_RESTORE = 2; // 0x2
+    field public static final int INSTALL_REASON_DEVICE_SETUP = 3; // 0x3
     field public static final int INSTALL_REASON_POLICY = 1; // 0x1
     field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
+    field public static final int INSTALL_REASON_USER = 4; // 0x4
     field public static final int MATCH_ALL = 131072; // 0x20000
     field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
     field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
@@ -13867,12 +13883,12 @@
   }
 
   public final class Icon implements android.os.Parcelable {
+    method public static android.graphics.drawable.Icon createWithAdaptiveBitmap(android.graphics.Bitmap);
     method public static android.graphics.drawable.Icon createWithBitmap(android.graphics.Bitmap);
     method public static android.graphics.drawable.Icon createWithContentUri(java.lang.String);
     method public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri);
     method public static android.graphics.drawable.Icon createWithData(byte[], int, int);
     method public static android.graphics.drawable.Icon createWithFilePath(java.lang.String);
-    method public static android.graphics.drawable.Icon createWithMaskableBitmap(android.graphics.Bitmap);
     method public static android.graphics.drawable.Icon createWithResource(android.content.Context, int);
     method public static android.graphics.drawable.Icon createWithResource(java.lang.String, int);
     method public int describeContents();
@@ -30221,12 +30237,12 @@
     field public static final int BATTERY_PLUGGED_AC = 1; // 0x1
     field public static final int BATTERY_PLUGGED_USB = 2; // 0x2
     field public static final int BATTERY_PLUGGED_WIRELESS = 4; // 0x4
-    field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6
     field public static final int BATTERY_PROPERTY_CAPACITY = 4; // 0x4
     field public static final int BATTERY_PROPERTY_CHARGE_COUNTER = 1; // 0x1
     field public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3; // 0x3
     field public static final int BATTERY_PROPERTY_CURRENT_NOW = 2; // 0x2
     field public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5; // 0x5
+    field public static final int BATTERY_PROPERTY_STATUS = 6; // 0x6
     field public static final int BATTERY_STATUS_CHARGING = 2; // 0x2
     field public static final int BATTERY_STATUS_DISCHARGING = 3; // 0x3
     field public static final int BATTERY_STATUS_FULL = 5; // 0x5
@@ -36550,7 +36566,6 @@
     field public static final int PURPOSE_ENCRYPT = 1; // 0x1
     field public static final int PURPOSE_SIGN = 4; // 0x4
     field public static final int PURPOSE_VERIFY = 8; // 0x8
-    field public static final int PURPOSE_WRAP_KEY = 16; // 0x10
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PSS = "PSS";
   }
@@ -36602,6 +36617,7 @@
 
   public abstract class AutoFillService extends android.app.Service {
     ctor public AutoFillService();
+    method public final void disableSelf();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
@@ -36664,6 +36680,7 @@
     method public android.service.autofill.SaveInfo.Builder addSavableIds(android.view.autofill.AutoFillId...);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
+    method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender);
   }
 
 }
@@ -37106,6 +37123,7 @@
     method public final void unlockAndRun(java.lang.Runnable);
     field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
     field public static final java.lang.String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
+    field public static final java.lang.String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT";
     field public static final java.lang.String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
   }
 
@@ -38415,6 +38433,7 @@
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
     field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
+    field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
     field public static final int PROPERTY_WIFI = 8; // 0x8
   }
 
@@ -38984,6 +39003,7 @@
     method public boolean handleMmi(java.lang.String);
     method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
     method public boolean isInCall();
+    method public boolean isInManagedCall();
     method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
     method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
     method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
@@ -39020,6 +39040,7 @@
     field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
     field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
     field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
+    field public static final java.lang.String METADATA_INCLUDE_SELF_MANAGED_CALLS = "android.telecom.INCLUDE_SELF_MANAGED_CALLS";
     field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
     field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
     field public static final int PRESENTATION_ALLOWED = 1; // 0x1
@@ -39707,6 +39728,7 @@
     method public java.lang.String getDeviceId();
     method public java.lang.String getDeviceId(int);
     method public java.lang.String getDeviceSoftwareVersion();
+    method public java.lang.String[] getForbiddenPlmns();
     method public java.lang.String getGroupIdLevel1();
     method public java.lang.String getIccAuthentication(int, int, java.lang.String);
     method public java.lang.String getLine1Number();
@@ -40558,6 +40580,7 @@
     method public boolean hasSystemFeature(java.lang.String);
     method public boolean hasSystemFeature(java.lang.String, int);
     method public boolean isInstantApp();
+    method public boolean isInstantApp(java.lang.String);
     method public boolean isPermissionReviewModeEnabled();
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
@@ -45160,6 +45183,7 @@
     method public float getAlpha();
     method public android.view.animation.Animation getAnimation();
     method public android.os.IBinder getApplicationWindowToken();
+    method public int getAutoFillHint();
     method public int getAutoFillMode();
     method public android.view.autofill.AutoFillType getAutoFillType();
     method public android.view.autofill.AutoFillValue getAutoFillValue();
@@ -45480,6 +45504,7 @@
     method public void setActivated(boolean);
     method public void setAlpha(float);
     method public void setAnimation(android.view.animation.Animation);
+    method public void setAutoFillHint(int);
     method public void setAutoFillMode(int);
     method public void setBackground(android.graphics.drawable.Drawable);
     method public void setBackgroundColor(int);
@@ -45621,6 +45646,20 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 512; // 0x200
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 4096; // 0x1000
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 1024; // 0x400
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = 2048; // 0x800
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_NUMBER = 128; // 0x80
+    field public static final int AUTO_FILL_HINT_CREDIT_CARD_SECURITY_CODE = 256; // 0x100
+    field public static final int AUTO_FILL_HINT_EMAIL_ADDRESS = 1; // 0x1
+    field public static final int AUTO_FILL_HINT_NAME = 2; // 0x2
+    field public static final int AUTO_FILL_HINT_NONE = 0; // 0x0
+    field public static final int AUTO_FILL_HINT_PASSWORD = 8; // 0x8
+    field public static final int AUTO_FILL_HINT_PHONE = 16; // 0x10
+    field public static final int AUTO_FILL_HINT_POSTAL_ADDRESS = 32; // 0x20
+    field public static final int AUTO_FILL_HINT_POSTAL_CODE = 64; // 0x40
+    field public static final int AUTO_FILL_HINT_USERNAME = 4; // 0x4
     field public static final int AUTO_FILL_MODE_AUTO = 1; // 0x1
     field public static final int AUTO_FILL_MODE_INHERIT = 0; // 0x0
     field public static final int AUTO_FILL_MODE_MANUAL = 2; // 0x2
@@ -46277,6 +46316,7 @@
     method public abstract void setAccessibilityFocused(boolean);
     method public abstract void setActivated(boolean);
     method public abstract void setAlpha(float);
+    method public abstract void setAutoFillHint(int);
     method public abstract void setAutoFillOptions(java.lang.String[]);
     method public abstract void setAutoFillType(android.view.autofill.AutoFillType);
     method public abstract void setAutoFillValue(android.view.autofill.AutoFillValue);
@@ -46294,6 +46334,7 @@
     method public abstract void setFocused(boolean);
     method public abstract void setHint(java.lang.CharSequence);
     method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String);
+    method public abstract void setInputType(int);
     method public abstract void setLongClickable(boolean);
     method public abstract void setSanitized(boolean);
     method public abstract void setSelected(boolean);
@@ -47547,10 +47588,11 @@
 
   public final class AutoFillType implements android.os.Parcelable {
     method public int describeContents();
+    method public static android.view.autofill.AutoFillType forDate();
     method public static android.view.autofill.AutoFillType forList();
-    method public static android.view.autofill.AutoFillType forText(int);
+    method public static android.view.autofill.AutoFillType forText();
     method public static android.view.autofill.AutoFillType forToggle();
-    method public int getSubType();
+    method public boolean isDate();
     method public boolean isList();
     method public boolean isText();
     method public boolean isToggle();
@@ -47560,9 +47602,11 @@
 
   public final class AutoFillValue implements android.os.Parcelable {
     method public int describeContents();
+    method public static android.view.autofill.AutoFillValue forDate(long);
     method public static android.view.autofill.AutoFillValue forList(int);
     method public static android.view.autofill.AutoFillValue forText(java.lang.CharSequence);
     method public static android.view.autofill.AutoFillValue forToggle(boolean);
+    method public long getDateValue();
     method public int getListValue();
     method public java.lang.CharSequence getTextValue();
     method public boolean getToggleValue();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index fda9966..cae37c6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3024,14 +3024,22 @@
 
         /**
          * Constant for {@link #importance}: This process process contains
-         * background code that is expendable.
+         * cached code that is expendable, not actively running any app components
+         * we care about.
          */
-        public static final int IMPORTANCE_BACKGROUND = 400;
+        public static final int IMPORTANCE_CACHED = 400;
+
+        /**
+         * @deprecated Renamed to {@link #IMPORTANCE_CACHED}.
+         */
+        public static final int IMPORTANCE_BACKGROUND = IMPORTANCE_CACHED;
 
         /**
          * Constant for {@link #importance}: This process is empty of any
          * actively running code.
+         * @deprecated This value is no longer reported, use {@link #IMPORTANCE_CACHED} instead.
          */
+        @Deprecated
         public static final int IMPORTANCE_EMPTY = 500;
 
         /**
@@ -3044,7 +3052,7 @@
             if (procState == PROCESS_STATE_NONEXISTENT) {
                 return IMPORTANCE_GONE;
             } else if (procState >= PROCESS_STATE_HOME) {
-                return IMPORTANCE_BACKGROUND;
+                return IMPORTANCE_CACHED;
             } else if (procState >= PROCESS_STATE_SERVICE) {
                 return IMPORTANCE_SERVICE;
             } else if (procState > PROCESS_STATE_HEAVY_WEIGHT) {
@@ -3066,7 +3074,7 @@
         public static int importanceToProcState(int importance) {
             if (importance == IMPORTANCE_GONE) {
                 return PROCESS_STATE_NONEXISTENT;
-            } else if (importance >= IMPORTANCE_BACKGROUND) {
+            } else if (importance >= IMPORTANCE_CACHED) {
                 return PROCESS_STATE_HOME;
             } else if (importance >= IMPORTANCE_SERVICE) {
                 return PROCESS_STATE_SERVICE;
@@ -3088,8 +3096,8 @@
         /**
          * The relative importance level that the system places on this
          * process.  May be one of {@link #IMPORTANCE_FOREGROUND},
-         * {@link #IMPORTANCE_VISIBLE}, {@link #IMPORTANCE_SERVICE},
-         * {@link #IMPORTANCE_BACKGROUND}, or {@link #IMPORTANCE_EMPTY}.  These
+         * {@link #IMPORTANCE_VISIBLE}, {@link #IMPORTANCE_SERVICE}, or
+         * {@link #IMPORTANCE_CACHED}.  These
          * constants are numbered so that "more important" values are always
          * smaller than "less important" values.
          */
@@ -3101,7 +3109,7 @@
          * utility of processes within a category.  This number means nothing
          * except that a smaller values are more recently used (and thus
          * more important).  Currently an LRU value is only maintained for
-         * the {@link #IMPORTANCE_BACKGROUND} category, though others may
+         * the {@link #IMPORTANCE_CACHED} category, though others may
          * be maintained in the future.
          */
         public int lru;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0608acb..64e2d49 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -805,9 +805,13 @@
 
     @Override
     public boolean isInstantApp() {
+        return isInstantApp(mContext.getPackageName());
+    }
+
+    @Override
+    public boolean isInstantApp(String packageName) {
         try {
-            return mPM.isInstantApp(mContext.getPackageName(),
-                    mContext.getUserId());
+            return mPM.isInstantApp(packageName, mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7ee93d0..0ab4b80 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2254,11 +2254,10 @@
     }
 
     void setResources(Resources r) {
-        if (mPackageInfo.getTargetSdkVersion() < VERSION_CODES.O) {
-            mResources = new CompatResources(r, this);
-        } else {
-            mResources = r;
+        if (r instanceof CompatResources) {
+            ((CompatResources) r).setContext(this);
         }
+        mResources = r;
     }
 
     void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
diff --git a/core/java/android/app/FragmentTransition.java b/core/java/android/app/FragmentTransition.java
index f62ab8d..780a922 100644
--- a/core/java/android/app/FragmentTransition.java
+++ b/core/java/android/app/FragmentTransition.java
@@ -780,8 +780,10 @@
             names = inTransaction.mSharedElementTargetNames;
         }
 
-        inSharedElements.retainAll(names);
-        if (sharedElementCallback != null) {
+        if (names != null) {
+            inSharedElements.retainAll(names);
+        }
+        if (names != null && sharedElementCallback != null) {
             sharedElementCallback.onMapSharedElements(names, inSharedElements);
             for (int i = names.size() - 1; i >= 0; i--) {
                 String name = names.get(i);
@@ -830,8 +832,9 @@
             FragmentContainerTransition fragments,
             Transition enterTransition, boolean inIsPop) {
         BackStackRecord inTransaction = fragments.lastInTransaction;
-        if (enterTransition != null && inTransaction.mSharedElementSourceNames != null &&
-                !inTransaction.mSharedElementSourceNames.isEmpty()) {
+        if (enterTransition != null && inSharedElements != null
+                && inTransaction.mSharedElementSourceNames != null
+                && !inTransaction.mSharedElementSourceNames.isEmpty()) {
             final String targetName = inIsPop
                     ? inTransaction.mSharedElementSourceNames.get(0)
                     : inTransaction.mSharedElementTargetNames.get(0);
@@ -1096,7 +1099,9 @@
         if (transition != null) {
             viewList = new ArrayList<>();
             View root = fragment.getView();
-            root.captureTransitioningViews(viewList);
+            if (root != null) {
+                root.captureTransitioningViews(viewList);
+            }
             if (sharedElements != null) {
                 viewList.removeAll(sharedElements);
             }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 8538330..dad2061 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -522,6 +522,7 @@
     void startLocalVoiceInteraction(in IBinder token, in Bundle options);
     void stopLocalVoiceInteraction(in IBinder token);
     boolean supportsLocalVoiceInteraction();
+    void notifyPinnedStackAnimationStarted();
     void notifyPinnedStackAnimationEnded();
     void removeStack(int stackId);
     void makePackageIdle(String packageName, int userId);
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 9834350..d8d4bb9 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -38,6 +38,11 @@
     void onPinnedActivityRestartAttempt(String launchedFromPackage);
 
     /**
+     * Called whenever the pinned stack is starting animating a resize.
+     */
+    void onPinnedStackAnimationStarted();
+
+    /**
      * Called whenever the pinned stack is done animating a resize.
      */
     void onPinnedStackAnimationEnded();
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 64fc44b..3fc459e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4040,15 +4040,23 @@
         }
 
         /**
-         * Construct a RemoteViews for the final notification header only
+         * Construct a RemoteViews for the final notification header only. This will not be
+         * colorized.
          *
          * @hide
          */
         public RemoteViews makeNotificationHeader() {
+            Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
+            mN.extras.putBoolean(EXTRA_COLORIZED, false);
             RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
                     R.layout.notification_template_header);
             resetNotificationHeader(header);
             bindNotificationHeader(header, false /* ambient */);
+            if (colorized != null) {
+                mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
+            } else {
+                mN.extras.remove(EXTRA_COLORIZED);
+            }
             return header;
         }
 
@@ -4167,21 +4175,14 @@
                     mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
                 }
             }
-            Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
-            mN.extras.putBoolean(EXTRA_COLORIZED, false);
 
             RemoteViews header = makeNotificationHeader();
-
+            header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true);
             if (summary != null) {
                 mN.extras.putCharSequence(EXTRA_SUB_TEXT, summary);
             } else {
                 mN.extras.remove(EXTRA_SUB_TEXT);
             }
-            if (colorized != null) {
-                mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
-            } else {
-                mN.extras.remove(EXTRA_COLORIZED);
-            }
             mN.color = color;
             return header;
         }
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 1a51608..85e6b85 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -20,8 +20,10 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlSerializer;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.annotation.SystemApi;
-import android.graphics.Color;
 import android.media.AudioAttributes;
 import android.net.Uri;
 import android.os.Parcel;
@@ -45,6 +47,7 @@
 
     private static final String TAG_CHANNEL = "channel";
     private static final String ATT_NAME = "name";
+    private static final String ATT_NAME_RES_ID = "name_res_id";
     private static final String ATT_ID = "id";
     private static final String ATT_DELETED = "deleted";
     private static final String ATT_PRIORITY = "priority";
@@ -138,6 +141,7 @@
 
     private final String mId;
     private CharSequence mName;
+    private int mNameResId = 0;
     private int mImportance = DEFAULT_IMPORTANCE;
     private boolean mBypassDnd;
     private int mLockscreenVisibility = DEFAULT_VISIBILITY;
@@ -156,7 +160,9 @@
      * Creates a notification channel.
      *
      * @param id The id of the channel. Must be unique per package.
-     * @param name The user visible name of the channel.
+     * @param name The user visible name of the channel. Unchangeable once created; use this
+     *             constructor if the channel represents a user-defined category that does not
+     *             need to be translated.
      * @param importance The importance of the channel. This controls how interruptive notifications
      *                   posted to this channel are. See e.g.
      *                   {@link NotificationManager#IMPORTANCE_DEFAULT}.
@@ -167,6 +173,21 @@
         this.mImportance = importance;
     }
 
+    /**
+     * Creates a notification channel.
+     *
+     * @param id The id of the channel. Must be unique per package.
+     * @param nameResId The resource id of the string containing the channel name.
+     * @param importance The importance of the channel. This controls how interruptive notifications
+     *                   posted to this channel are. See e.g.
+     *                   {@link NotificationManager#IMPORTANCE_DEFAULT}.
+     */
+    public NotificationChannel(String id, @StringRes int nameResId, int importance) {
+        this.mId = id;
+        this.mNameResId = nameResId;
+        this.mImportance = importance;
+    }
+
     protected NotificationChannel(Parcel in) {
         if (in.readByte() != 0) {
             mId = in.readString();
@@ -174,6 +195,7 @@
             mId = null;
         }
         mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mNameResId = in.readInt();
         mImportance = in.readInt();
         mBypassDnd = in.readByte() != 0;
         mLockscreenVisibility = in.readInt();
@@ -206,6 +228,7 @@
             dest.writeByte((byte) 0);
         }
         TextUtils.writeToParcel(mName, dest, flags);
+        dest.writeInt(mNameResId);
         dest.writeInt(mImportance);
         dest.writeByte(mBypassDnd ? (byte) 1 : (byte) 0);
         dest.writeInt(mLockscreenVisibility);
@@ -382,11 +405,18 @@
     /**
      * Returns the user visible name of this channel.
      */
-    public CharSequence getName() {
+    public @Nullable CharSequence getName() {
         return mName;
     }
 
     /**
+     * Returns the resource id of the user visible name of this channel.
+     */
+    public int getNameResId() {
+        return mNameResId;
+    }
+
+    /**
      * Returns the user specified importance {e.g. @link NotificationManager#IMPORTANCE_LOW} for
      * notifications posted to this channel.
      */
@@ -516,7 +546,10 @@
     public void writeXml(XmlSerializer out) throws IOException {
         out.startTag(null, TAG_CHANNEL);
         out.attribute(null, ATT_ID, getId());
-        out.attribute(null, ATT_NAME, getName().toString());
+        if (getName() != null) {
+            out.attribute(null, ATT_NAME, getName().toString());
+        }
+        out.attribute(null, ATT_NAME_RES_ID, Integer.toString(getNameResId()));
         if (getImportance() != DEFAULT_IMPORTANCE) {
             out.attribute(
                     null, ATT_IMPORTANCE, Integer.toString(getImportance()));
@@ -574,6 +607,7 @@
         JSONObject record = new JSONObject();
         record.put(ATT_ID, getId());
         record.put(ATT_NAME, getName());
+        record.put(ATT_NAME_RES_ID, getNameResId());
         if (getImportance() != DEFAULT_IMPORTANCE) {
             record.put(ATT_IMPORTANCE,
                     NotificationListenerService.Ranking.importanceToString(getImportance()));
@@ -691,6 +725,7 @@
 
         NotificationChannel that = (NotificationChannel) o;
 
+        if (getNameResId() != that.getNameResId()) return false;
         if (getImportance() != that.getImportance()) return false;
         if (mBypassDnd != that.mBypassDnd) return false;
         if (getLockscreenVisibility() != that.getLockscreenVisibility()) return false;
@@ -720,6 +755,7 @@
     public int hashCode() {
         int result = getId() != null ? getId().hashCode() : 0;
         result = 31 * result + (getName() != null ? getName().hashCode() : 0);
+        result = 31 * result + getNameResId();
         result = 31 * result + getImportance();
         result = 31 * result + (mBypassDnd ? 1 : 0);
         result = 31 * result + getLockscreenVisibility();
@@ -741,6 +777,7 @@
         return "NotificationChannel{" +
                 "mId='" + mId + '\'' +
                 ", mName=" + mName +
+                ", mNameResId=" + mNameResId +
                 ", mImportance=" + mImportance +
                 ", mBypassDnd=" + mBypassDnd +
                 ", mLockscreenVisibility=" + mLockscreenVisibility +
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 8854adc..288d39a 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -15,6 +15,7 @@
  */
 package android.app;
 
+import android.annotation.StringRes;
 import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Parcel;
@@ -40,23 +41,38 @@
 
     private static final String TAG_GROUP = "channelGroup";
     private static final String ATT_NAME = "name";
+    private static final String ATT_NAME_RES_ID = "name_res_id";
     private static final String ATT_ID = "id";
 
     private final String mId;
     private CharSequence mName;
+    private int mNameResId = 0;
     private List<NotificationChannel> mChannels = new ArrayList<>();
 
     /**
      * Creates a notification channel.
      *
      * @param id The id of the group. Must be unique per package.
-     * @param name The user visible name of the group.
+     * @param name The user visible name of the group. Unchangeable once created; use this
+     *             constructor if the group represents something user-defined that does not
+     *             need to be translated.
      */
     public NotificationChannelGroup(String id, CharSequence name) {
         this.mId = id;
         this.mName = name;
     }
 
+    /**
+     * Creates a notification channel.
+     *
+     * @param id The id of the group. Must be unique per package.
+     * @param nameResId String resource id of the user visible name of the group.
+     */
+    public NotificationChannelGroup(String id, @StringRes int nameResId) {
+        this.mId = id;
+        this.mNameResId = nameResId;
+    }
+
     protected NotificationChannelGroup(Parcel in) {
         if (in.readByte() != 0) {
             mId = in.readString();
@@ -64,6 +80,7 @@
             mId = null;
         }
         mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mNameResId = in.readInt();
         in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader());
     }
 
@@ -76,6 +93,7 @@
             dest.writeByte((byte) 0);
         }
         TextUtils.writeToParcel(mName, dest, flags);
+        dest.writeInt(mNameResId);
         dest.writeParcelableList(mChannels, flags);
     }
 
@@ -93,6 +111,13 @@
         return mName;
     }
 
+    /**
+     * Returns the resource id of the user visible name of this group.
+     */
+    public @StringRes int getNameResId() {
+        return mNameResId;
+    }
+
     /*
      * Returns the list of channels that belong to this group
      *
@@ -119,7 +144,12 @@
         out.startTag(null, TAG_GROUP);
 
         out.attribute(null, ATT_ID, getId());
-        out.attribute(null, ATT_NAME, getName().toString());
+        if (getName() != null) {
+            out.attribute(null, ATT_NAME, getName().toString());
+        }
+        if (getNameResId() != 0) {
+            out.attribute(null, ATT_NAME_RES_ID, Integer.toString(getNameResId()));
+        }
 
         out.endTag(null, TAG_GROUP);
     }
@@ -132,6 +162,7 @@
         JSONObject record = new JSONObject();
         record.put(ATT_ID, getId());
         record.put(ATT_NAME, getName());
+        record.put(ATT_NAME_RES_ID, getNameResId());
         return record;
     }
 
@@ -160,6 +191,7 @@
 
         NotificationChannelGroup that = (NotificationChannelGroup) o;
 
+        if (getNameResId() != that.getNameResId()) return false;
         if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;
         if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
             return false;
@@ -171,13 +203,18 @@
 
     @Override
     public NotificationChannelGroup clone() {
-        return new NotificationChannelGroup(getId(), getName());
+        if (getName() != null) {
+            return new NotificationChannelGroup(getId(), getName());
+        } else {
+            return new NotificationChannelGroup(getId(), getNameResId());
+        }
     }
 
     @Override
     public int hashCode() {
         int result = getId() != null ? getId().hashCode() : 0;
         result = 31 * result + (getName() != null ? getName().hashCode() : 0);
+        result = 31 * result + getNameResId();
         result = 31 * result + (getChannels() != null ? getChannels().hashCode() : 0);
         return result;
     }
@@ -187,6 +224,8 @@
         return "NotificationChannelGroup{" +
                 "mId='" + mId + '\'' +
                 ", mName=" + mName +
+                ", mNameResId=" + mNameResId +
+                ", mChannels=" + mChannels +
                 '}';
     }
 }
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 52ec045..b42df5e 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.content.pm.ActivityInfo;
 import android.content.res.AssetManager;
+import android.content.res.CompatResources;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -447,7 +448,8 @@
      * or the class loader is different.
      */
     private @NonNull Resources getOrCreateResourcesForActivityLocked(@NonNull IBinder activityToken,
-            @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl) {
+            @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl,
+            @NonNull CompatibilityInfo compatInfo) {
         final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
                 activityToken);
 
@@ -466,7 +468,8 @@
             }
         }
 
-        Resources resources = new Resources(classLoader);
+        Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
+                : new Resources(classLoader);
         resources.setImpl(impl);
         activityResources.activityResources.add(new WeakReference<>(resources));
         if (DEBUG) {
@@ -481,7 +484,7 @@
      * otherwise creates a new Resources object.
      */
     private @NonNull Resources getOrCreateResourcesLocked(@NonNull ClassLoader classLoader,
-            @NonNull ResourcesImpl impl) {
+            @NonNull ResourcesImpl impl, @NonNull CompatibilityInfo compatInfo) {
         // Find an existing Resources that has this ResourcesImpl set.
         final int refCount = mResourceReferences.size();
         for (int i = 0; i < refCount; i++) {
@@ -498,7 +501,8 @@
         }
 
         // Create a new Resources reference and use the existing ResourcesImpl object.
-        Resources resources = new Resources(classLoader);
+        Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
+                : new Resources(classLoader);
         resources.setImpl(impl);
         mResourceReferences.add(new WeakReference<>(resources));
         if (DEBUG) {
@@ -614,7 +618,7 @@
                         Slog.d(TAG, "- using existing impl=" + resourcesImpl);
                     }
                     return getOrCreateResourcesForActivityLocked(activityToken, classLoader,
-                            resourcesImpl);
+                            resourcesImpl, key.mCompatInfo);
                 }
 
                 // We will create the ResourcesImpl object outside of holding this lock.
@@ -629,7 +633,7 @@
                     if (DEBUG) {
                         Slog.d(TAG, "- using existing impl=" + resourcesImpl);
                     }
-                    return getOrCreateResourcesLocked(classLoader, resourcesImpl);
+                    return getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
                 }
 
                 // We will create the ResourcesImpl object outside of holding this lock.
@@ -659,9 +663,9 @@
             final Resources resources;
             if (activityToken != null) {
                 resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
-                        resourcesImpl);
+                        resourcesImpl, key.mCompatInfo);
             } else {
-                resources = getOrCreateResourcesLocked(classLoader, resourcesImpl);
+                resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
             }
             return resources;
         }
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 5a0845f..7a569fc 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -39,6 +39,10 @@
     }
 
     @Override
+    public void onPinnedStackAnimationStarted() throws RemoteException {
+    }
+
+    @Override
     public void onPinnedStackAnimationEnded() throws RemoteException {
     }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6585793..449cca3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -7958,4 +7958,28 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+
+    /**
+     * Called by the system to get a list of CA certificates that were installed by the device or
+     * profile owner.
+     *
+     * <p> The caller must be the target user's Device Owner/Profile owner or hold the
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
+     *
+     * @param user The user for whom to retrieve information.
+     * @return list of aliases identifying CA certificates installed by the device or profile owner
+     * @throws SecurityException if the caller does not have permission to retrieve information
+     *         about the given user's CA certificates.
+     *
+     * @hide
+     */
+    @TestApi
+    public List<String> getOwnerInstalledCaCerts(@NonNull UserHandle user) {
+        try {
+            return mService.getOwnerInstalledCaCerts(user).getList();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index ec97c2c..97a4678 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -27,6 +27,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.StringParceledListSlice;
 import android.graphics.Bitmap;
 import android.net.ProxyInfo;
 import android.net.Uri;
@@ -349,4 +350,5 @@
     boolean resetPasswordWithToken(in ComponentName admin, String password, in byte[] token, int flags);
 
     boolean isDefaultInputMethodSetByOwner(in UserHandle user);
+    StringParceledListSlice getOwnerInstalledCaCerts(in UserHandle user);
 }
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 483a7a1..a1ef4a6 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -26,6 +26,7 @@
 import android.view.autofill.AutoFillValue;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Assist data automatically created by the platform's implementation
@@ -533,13 +534,16 @@
         String mIdPackage;
         String mIdType;
         String mIdEntry;
+
         // TODO(b/33197203): once we have more flags, it might be better to store the individual
         // fields (viewId and childId) of the field.
         AutoFillId mAutoFillId;
         AutoFillType mAutoFillType;
+        @View.AutoFillHint int mAutoFillHint;
         AutoFillValue mAutoFillValue;
         String[] mAutoFillOptions;
         boolean mSanitized;
+
         int mX;
         int mY;
         int mScrollX;
@@ -581,6 +585,7 @@
         static final int FLAGS_HAS_ID = 0x00200000;
         static final int FLAGS_HAS_CHILDREN = 0x00100000;
         static final int FLAGS_HAS_URL = 0x00080000;
+        static final int FLAGS_HAS_INPUT_TYPE = 0x00040000;
         static final int FLAGS_ALL_CONTROL = 0xfff00000;
 
         int mFlags;
@@ -589,6 +594,7 @@
         CharSequence mContentDescription;
 
         ViewNodeText mText;
+        int mInputType;
         String mUrl;
         Bundle mExtras;
 
@@ -618,6 +624,7 @@
                 mSanitized = in.readInt() == 1;
                 mAutoFillId = in.readParcelable(null);
                 mAutoFillType = in.readParcelable(null);
+                mAutoFillHint = in.readInt();
                 mAutoFillValue = in.readParcelable(null);
                 mAutoFillOptions = in.readStringArray();
             }
@@ -655,6 +662,9 @@
             if ((flags&FLAGS_HAS_TEXT) != 0) {
                 mText = new ViewNodeText(in, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0);
             }
+            if ((flags&FLAGS_HAS_INPUT_TYPE) != 0) {
+                mInputType = in.readInt();
+            }
             if ((flags&FLAGS_HAS_URL) != 0) {
                 mUrl = in.readString();
             }
@@ -711,6 +721,9 @@
                     flags |= FLAGS_HAS_COMPLEX_TEXT;
                 }
             }
+            if (mInputType != 0) {
+                flags |= FLAGS_HAS_INPUT_TYPE;
+            }
             if (mUrl != null) {
                 flags |= FLAGS_HAS_URL;
             }
@@ -745,6 +758,7 @@
                 out.writeInt(mSanitized ? 1 : 0);
                 out.writeParcelable(mAutoFillId, 0);
                 out.writeParcelable(mAutoFillType,  0);
+                out.writeInt(mAutoFillHint);
                 final AutoFillValue sanitizedValue = writeSensitive ? mAutoFillValue : null;
                 out.writeParcelable(sanitizedValue,  0);
                 out.writeStringArray(mAutoFillOptions);
@@ -778,6 +792,10 @@
             if ((flags&FLAGS_HAS_TEXT) != 0) {
                 mText.writeToParcel(out, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0, writeSensitive);
             }
+            if ((flags&FLAGS_HAS_INPUT_TYPE) != 0) {
+                out.writeInt(mInputType);
+            }
+
             if ((flags&FLAGS_HAS_URL) != 0) {
                 out.writeString(mUrl);
             }
@@ -844,6 +862,20 @@
         }
 
         /**
+         * Describes the content of a view so that a auto-fill service can fill in the appropriate
+         * data.
+         *
+         * <p>It's only set when the {@link AssistStructure} is used for auto-filling purposes, not
+         * for assist.</p>
+         *
+         * @return The hint for this view
+         */
+        // TODO(b/35364993): add CTS/unit test
+        @View.AutoFillHint public int getAutoFillHint() {
+            return mAutoFillHint;
+        }
+
+        /**
          * Gets the the value of this view.
          *
          * <p>It's only set when the {@link AssistStructure} is used for auto-filling purposes, not
@@ -867,6 +899,15 @@
             return mAutoFillOptions;
         }
 
+        /**
+         * Gets the {@link android.text.InputType} bits of this structure.
+         *
+         * @return bits as defined by {@link android.text.InputType}.
+         */
+        public int getInputType() {
+            return mInputType;
+        }
+
         /** @hide */
         public boolean isSanitized() {
             return mSanitized;
@@ -1525,6 +1566,11 @@
         }
 
         @Override
+        public void setAutoFillHint(@View.AutoFillHint int hint) {
+            mNode.mAutoFillHint = hint;
+        }
+
+        @Override
         public void setAutoFillValue(AutoFillValue value) {
             mNode.mAutoFillValue = value;
         }
@@ -1534,6 +1580,11 @@
             mNode.mAutoFillOptions = options;
         }
 
+        @Override
+        public void setInputType(int inputType) {
+            mNode.mInputType = inputType;
+        }
+
         /**
          * @hide
          */
@@ -1664,6 +1715,9 @@
         } else {
             Log.i(TAG, prefix + "AutoFill info: id= " + autoFillId
                     + ", type=" + node.getAutoFillType()
+                    + ", options=" + Arrays.toString(node.getAutoFillOptions())
+                    + ", inputType=" + node.getInputType()
+                    + ", hint=" + Integer.toHexString(node.getAutoFillHint())
                     + ", value=" + node.getAutoFillValue()
                     + ", sanitized=" + node.isSanitized());
         }
diff --git a/core/java/android/app/backup/BackupManagerMonitor.java b/core/java/android/app/backup/BackupManagerMonitor.java
index d2a623e..ebad16e 100644
--- a/core/java/android/app/backup/BackupManagerMonitor.java
+++ b/core/java/android/app/backup/BackupManagerMonitor.java
@@ -54,26 +54,118 @@
   public static final String EXTRA_LOG_EVENT_CATEGORY =
           "android.app.backup.extra.LOG_EVENT_CATEGORY";
 
+
   /**
-   * string: when we have event of id LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER we send the version
+   * boolean: when we have an event with id LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL we record if
+   * the call was to cancel backup of all packages
+   */
+  public static final String EXTRA_LOG_CANCEL_ALL = "android.app.backup.extra.LOG_CANCEL_ALL";
+
+  /**
+   * string: when we have an event with id LOG_EVENT_ID_ILLEGAL_KEY we send the key that was used
+   * by the app
+   */
+  public static final String EXTRA_LOG_ILLEGAL_KEY = "android.app.backup.extra.LOG_ILLEGAL_KEY";
+
+  /**
+   * long: when we have an event with id LOG_EVENT_ID_ERROR_PREFLIGHT we send the error code that
+   * was returned by the transport during preflight
+   */
+  public static final String EXTRA_LOG_PREFLIGHT_ERROR =
+          "android.app.backup.extra.LOG_PREFLIGHT_ERROR";
+
+  /**
+   * string: when we have an event with id LOG_EVENT_ID_EXCEPTION_FULL_BACKUP we send the
+   * exception's stacktrace
+   */
+  public static final String EXTRA_LOG_EXCEPTION_FULL_BACKUP =
+          "android.app.backup.extra.LOG_EXCEPTION_FULL_BACKUP";
+
+  /**
+   * int: when we have an event with id LOG_EVENT_ID_RESTORE_VERSION_HIGHER we send the
+   * restore package version
+   */
+  public static final String EXTRA_LOG_RESTORE_VERSION =
+          "android.app.backup.extra.LOG_RESTORE_VERSION";
+
+  /**
+   * boolean: when we have an event with id LOG_EVENT_ID_RESTORE_VERSION_HIGHER we record if
+   * ApplicationInfo.FLAG_RESTORE_ANY_VERSION flag is set
+   */
+  public static final String EXTRA_LOG_RESTORE_ANYWAY =
+          "android.app.backup.extra.LOG_RESTORE_ANYWAY";
+
+
+  /**
+   * boolean: when we have an event with id LOG_EVENT_ID_APK_NOT_INSTALLED we record if
+   * the policy allows to install apks provided with the dataset
+   */
+  public static final String EXTRA_LOG_POLICY_ALLOW_APKS =
+          "android.app.backup.extra.LOG_POLICY_ALLOW_APKS";
+
+
+  /**
+   * string: when we have an event with id LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE we record the
+   * package name provided in the restore manifest
+   */
+  public static final String EXTRA_LOG_MANIFEST_PACKAGE_NAME =
+          "android.app.backup.extra.LOG_MANIFEST_PACKAGE_NAME";
+
+  /**
+   * string: when we have an event with id LOG_EVENT_ID_WIDGET_METADATA_MISMATCH we record the
+   * package name provided in the widget metadata
+   */
+  public static final String EXTRA_LOG_WIDGET_PACKAGE_NAME =
+          "android.app.backup.extra.LOG_WIDGET_PACKAGE_NAME";
+
+  /**
+   * int: when we have event of id LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER we send the version
    * of the backup.
    */
-  public static final String EXTRA_LOG_OLD_VERSION =
-          "android.app.backup.extra.LOG_OLD_VERSION";
+  public static final String EXTRA_LOG_OLD_VERSION = "android.app.backup.extra.LOG_OLD_VERSION";
 
   // TODO complete this list with all log messages. And document properly.
-  public static final int LOG_EVENT_ID_FULL_BACKUP_TIMEOUT = 4;
+  public static final int LOG_EVENT_ID_FULL_BACKUP_CANCEL = 4;
+  public static final int LOG_EVENT_ID_ILLEGAL_KEY = 5;
+  public static final int LOG_EVENT_ID_NO_DATA_TO_SEND = 7;
+  public static final int LOG_EVENT_ID_PACKAGE_INELIGIBLE = 9;
+  public static final int LOG_EVENT_ID_PACKAGE_KEY_VALUE_PARTICIPANT = 10;
+  public static final int LOG_EVENT_ID_PACKAGE_STOPPED = 11;
   public static final int LOG_EVENT_ID_PACKAGE_NOT_FOUND = 12;
+  public static final int LOG_EVENT_ID_BACKUP_DISABLED = 13;
+  public static final int LOG_EVENT_ID_DEVICE_NOT_PROVISIONED = 14;
   public static final int LOG_EVENT_ID_PACKAGE_TRANSPORT_NOT_PRESENT = 15;
-  public static final int LOG_EVENT_ID_KEY_VALUE_BACKUP_TIMEOUT = 21;
+  public static final int LOG_EVENT_ID_ERROR_PREFLIGHT = 16;
+  public static final int LOG_EVENT_ID_QUOTA_HIT_PREFLIGHT = 18;
+  public static final int LOG_EVENT_ID_EXCEPTION_FULL_BACKUP = 19;
+  public static final int LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL = 21;
   public static final int LOG_EVENT_ID_NO_RESTORE_METADATA_AVAILABLE = 22;
+  public static final int LOG_EVENT_ID_NO_PM_METADATA_RECEIVED = 23;
+  public static final int LOG_EVENT_ID_PM_AGENT_HAS_NO_METADATA = 24;
+  public static final int LOG_EVENT_ID_LOST_TRANSPORT = 25;
   public static final int LOG_EVENT_ID_PACKAGE_NOT_PRESENT = 26;
+  public static final int LOG_EVENT_ID_RESTORE_VERSION_HIGHER = 27;
   public static final int LOG_EVENT_ID_APP_HAS_NO_AGENT = 28;
+  public static final int LOG_EVENT_ID_SIGNATURE_MISMATCH = 29;
   public static final int LOG_EVENT_ID_CANT_FIND_AGENT = 30;
   public static final int LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT = 31;
+  public static final int LOG_EVENT_ID_RESTORE_ANY_VERSION = 34;
+  public static final int LOG_EVENT_ID_VERSIONS_MATCH = 35;
   public static final int LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER = 36;
+  public static final int LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH = 37;
+  public static final int LOG_EVENT_ID_SYSTEM_APP_NO_AGENT = 38;
+  public static final int LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE = 39;
+  public static final int LOG_EVENT_ID_APK_NOT_INSTALLED = 40;
+  public static final int LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK = 41;
+  public static final int LOG_EVENT_ID_MISSING_SIGNATURE = 42;
+  public static final int LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE = 43;
+  public static final int LOG_EVENT_ID_UNKNOWN_VERSION = 44;
   public static final int LOG_EVENT_ID_FULL_RESTORE_TIMEOUT = 45;
+  public static final int LOG_EVENT_ID_CORRUPT_MANIFEST = 46;
+  public static final int LOG_EVENT_ID_WIDGET_METADATA_MISMATCH = 47;
+  public static final int LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION = 48;
   public static final int LOG_EVENT_ID_NO_PACKAGES = 49;
+  public static final int LOG_EVENT_ID_TRANSPORT_IS_NULL = 50;
 
 
 
diff --git a/core/java/android/app/usage/CacheQuotaHint.java b/core/java/android/app/usage/CacheQuotaHint.java
index 4b6f99b..1d5c2b0 100644
--- a/core/java/android/app/usage/CacheQuotaHint.java
+++ b/core/java/android/app/usage/CacheQuotaHint.java
@@ -24,8 +24,10 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.util.Objects;
+
 /**
- * CacheQuotaRequest represents a triplet of a uid, the volume UUID it is stored upon, and
+ * CacheQuotaHint represents a triplet of a uid, the volume UUID it is stored upon, and
  * its usage stats. When processed, it obtains a cache quota as defined by the system which
  * allows apps to understand how much cache to use.
  * {@hide}
@@ -78,6 +80,23 @@
         return 0;
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof CacheQuotaHint) {
+            final CacheQuotaHint other = (CacheQuotaHint) o;
+            return Objects.equals(mUuid, other.mUuid)
+                    && Objects.equals(mUsageStats, other.mUsageStats)
+                    && mUid == other.mUid && mQuota == other.mQuota;
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(this.mUuid, this.mUid, this.mUsageStats, this.mQuota);
+    }
+
     public static final class Builder {
         private String mUuid;
         private int mUid;
@@ -100,7 +119,7 @@
         }
 
         public @NonNull Builder setUid(int uid) {
-            Preconditions.checkArgumentPositive(uid, "Proposed uid was not positive.");
+            Preconditions.checkArgumentNonnegative(uid, "Proposed uid was negative.");
             mUid = uid;
             return this;
         }
@@ -117,7 +136,6 @@
         }
 
         public @NonNull CacheQuotaHint build() {
-            Preconditions.checkNotNull(mUsageStats);
             return new CacheQuotaHint(this);
         }
     }
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 4252482..7d8459c 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -50,6 +50,12 @@
     public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
             "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
 
+    /* Extras used in ACTION_MESSAGE_RECEIVED intent */
+    public static final String EXTRA_SENDER_CONTACT_URI =
+            "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI";
+    public static final String EXTRA_SENDER_CONTACT_NAME =
+            "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME";
+
     private IBluetoothMapClient mService;
     private final Context mContext;
     private ServiceListener mServiceListener;
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9958a79..5579c9a 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -307,6 +307,18 @@
      */
     public static final String QUERY_ARG_SORT_COLLATION = "android:query-sort-collation";
 
+    /**
+     * Allows provider to report back to client which keys were honored.
+     *
+     * Key identifying a {@code String[]} containing all QUERY_ARG_SORT* arguments
+     * honored by the provider. Include this in {@link Cursor} extras {@link Bundle}
+     * when any QUERY_ARG_SORT* value was honored during the preparation of the
+     * results {@link Cursor}.
+     *
+     * @see #QUERY_ARG_SORT_COLUMNS, #QUERY_ARG_SORT_DIRECTION, #QUERY_ARG_SORT_COLLATION.
+     */
+    public static final String EXTRA_HONORED_ARGS = "android.content.extra.HONORED_ARGS";
+
     /** @hide */
     @IntDef(flag = false, value = {
             QUERY_SORT_DIRECTION_ASCENDING,
@@ -354,8 +366,9 @@
     public static final String QUERY_ARG_LIMIT = "android:query-page-limit";
 
     /**
-     * Added to {@link Cursor} extras {@link Bundle} to indicate size of the
-     * full, un-offset, un-limited recordset.
+     * Added to {@link Cursor} extras {@link Bundle} to indicate total size of
+     * recordset when paging is active. Providers must include this when
+     * implementing paging support.
      *
      * <p>When full size of the recordset is unknown a provider may return -1
      * to indicate this.
@@ -364,7 +377,7 @@
      * send content change notification once (if) full recordset size becomes
      * known.
      */
-    public static final String QUERY_RESULT_SIZE = "android:query-result-size";
+    public static final String EXTRA_TOTAL_SIZE = "android.content.extra.TOTAL_SIZE";
 
     /**
      * This is the Android platform's base MIME type for a content: URI
@@ -704,6 +717,13 @@
      * <li>Provide an explicit projection, to prevent reading data from storage
      * that aren't going to be used.
      *
+     * Provider must identify which QUERY_ARG_SORT* arguments were honored during
+     * the preparation of the result set by including the respective argument keys
+     * in the {@link Cursor} extras {@link Bundle}. See {@link #EXTRA_HONORED_ARGS}
+     * for details.
+     *
+     * @see #QUERY_ARG_SORT_COLUMNS, #QUERY_ARG_SORT_DIRECTION, #QUERY_ARG_SORT_COLLATION.
+     *
      * @param uri The URI, using the content:// scheme, for the content to
      *         retrieve.
      * @param projection A list of which columns to return. Passing null will
@@ -3037,17 +3057,19 @@
             query += " COLLATE NOCASE";
         }
 
-        switch (queryArgs.getInt(
-                QUERY_ARG_SORT_DIRECTION, Integer.MIN_VALUE)) {
-            case QUERY_SORT_DIRECTION_ASCENDING:
-                query += " ASC";
-                break;
-            case QUERY_SORT_DIRECTION_DESCENDING:
-                query += " DESC";
-                break;
-            default:
-                throw new IllegalArgumentException("Unsupported sort direction value."
-                        + " See ContentResolver documentation for details.");
+        int sortDir = queryArgs.getInt(QUERY_ARG_SORT_DIRECTION, Integer.MIN_VALUE);
+        if (sortDir != Integer.MIN_VALUE) {
+            switch (sortDir) {
+                case QUERY_SORT_DIRECTION_ASCENDING:
+                    query += " ASC";
+                    break;
+                case QUERY_SORT_DIRECTION_DESCENDING:
+                    query += " DESC";
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported sort direction value."
+                            + " See ContentResolver documentation for details.");
+            }
         }
         return query;
     }
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 776492a..c3fd089 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -829,7 +829,7 @@
                 final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
                 if (bmp != null) {
                     BitmapDrawable dr = new BitmapDrawable(mContext.getResources(), bmp);
-                    if (shortcut.hasMaskableBitmap()) {
+                    if (shortcut.hasAdaptiveBitmap()) {
                         return new AdaptiveIconDrawable(null, dr);
                     } else {
                         return dr;
@@ -854,7 +854,7 @@
                             icon.getResId(), shortcut.getUserHandle(), density);
                 }
                 case Icon.TYPE_BITMAP:
-                case Icon.TYPE_BITMAP_MASKABLE: {
+                case Icon.TYPE_ADAPTIVE_BITMAP: {
                     return icon.loadDrawable(mContext);
                 }
                 default:
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 0482f51..b3b5bcf 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -745,7 +745,8 @@
     public static final int DONT_KILL_APP = 0x00000001;
 
     /** @hide */
-    @IntDef({INSTALL_REASON_UNKNOWN, INSTALL_REASON_POLICY})
+    @IntDef({INSTALL_REASON_UNKNOWN, INSTALL_REASON_POLICY, INSTALL_REASON_DEVICE_RESTORE,
+            INSTALL_REASON_DEVICE_SETUP, INSTALL_REASON_USER})
     @Retention(RetentionPolicy.SOURCE)
     public @interface InstallReason {}
 
@@ -760,6 +761,21 @@
     public static final int INSTALL_REASON_POLICY = 1;
 
     /**
+     * Code indicating that this package was installed as part of restoring from another device.
+     */
+    public static final int INSTALL_REASON_DEVICE_RESTORE = 2;
+
+    /**
+     * Code indicating that this package was installed as part of device setup.
+     */
+    public static final int INSTALL_REASON_DEVICE_SETUP = 3;
+
+    /**
+     * Code indicating that the package installation was initiated by the user.
+     */
+    public static final int INSTALL_REASON_USER = 4;
+
+    /**
      * Installation return code: this is passed to the
      * {@link IPackageInstallObserver} on success.
      *
@@ -3737,10 +3753,11 @@
     public abstract @Nullable Drawable getInstantAppIcon(String packageName);
 
     /**
-     * Gets whether the caller is an instant app.
+     * Gets whether this application is an instant app.
      *
      * @return Whether caller is an instant app.
      *
+     * @see #isInstantApp(String)
      * @see #setInstantAppCookie(byte[])
      * @see #getInstantAppCookie()
      * @see #getInstantAppCookieMaxSize()
@@ -3748,12 +3765,26 @@
     public abstract boolean isInstantApp();
 
     /**
+     * Gets whether the given package is an instant app.
+     *
+     * @param packageName The package to check
+     * @return Whether the given package is an instant app.
+     *
+     * @see #isInstantApp()
+     * @see #setInstantAppCookie(byte[])
+     * @see #getInstantAppCookie()
+     * @see #getInstantAppCookieMaxSize()
+     */
+    public abstract boolean isInstantApp(String packageName);
+
+    /**
      * Gets the maximum size in bytes of the cookie data an instant app
      * can store on the device.
      *
      * @return The max cookie size in bytes.
      *
      * @see #isInstantApp()
+     * @see #isInstantApp(String)
      * @see #setInstantAppCookie(byte[])
      * @see #getInstantAppCookie()
      */
@@ -3770,6 +3801,7 @@
      * @return The cookie.
      *
      * @see #isInstantApp()
+     * @see #isInstantApp(String)
      * @see #setInstantAppCookie(byte[])
      * @see #getInstantAppCookieMaxSize()
      */
@@ -3792,6 +3824,7 @@
      * @return Whether the cookie was set.
      *
      * @see #isInstantApp()
+     * @see #isInstantApp(String)
      * @see #getInstantAppCookieMaxSize()
      * @see #getInstantAppCookie()
      */
@@ -4983,6 +5016,7 @@
      */
     public PackageInfo getPackageArchiveInfo(String archiveFilePath, @PackageInfoFlags int flags) {
         final PackageParser parser = new PackageParser();
+        parser.setCallback(new PackageParser.CallbackImpl(this));
         final File apkFile = new File(archiveFilePath);
         try {
             if ((flags & (MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE)) != 0) {
@@ -6108,6 +6142,9 @@
      *
      * @see #INSTALL_REASON_UNKNOWN
      * @see #INSTALL_REASON_POLICY
+     * @see #INSTALL_REASON_DEVICE_RESTORE
+     * @see #INSTALL_REASON_DEVICE_SETUP
+     * @see #INSTALL_REASON_USER
      *
      * @hide
      */
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f801e45..60cc6b0 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -285,6 +285,7 @@
     private String[] mSeparateProcesses;
     private boolean mOnlyCoreApps;
     private DisplayMetrics mMetrics;
+    private Callback mCallback;
     private File mCacheDir;
 
     private static final int SDK_VERSION = Build.VERSION.SDK_INT;
@@ -506,6 +507,37 @@
         mCacheDir = cacheDir;
     }
 
+    /**
+     * Callback interface for retrieving information that may be needed while parsing
+     * a package.
+     */
+    public interface Callback {
+        boolean hasFeature(String feature);
+    }
+
+    /**
+     * Standard implementation of {@link Callback} on top of the public {@link PackageManager}
+     * class.
+     */
+    public static final class CallbackImpl implements Callback {
+        private final PackageManager mPm;
+
+        public CallbackImpl(PackageManager pm) {
+            mPm = pm;
+        }
+
+        @Override public boolean hasFeature(String feature) {
+            return mPm.hasSystemFeature(feature);
+        }
+    }
+
+    /**
+     * Set the {@link Callback} that can be used while parsing.
+     */
+    public void setCallback(Callback cb) {
+        mCallback = cb;
+    }
+
     public static final boolean isApkFile(File file) {
         return isApkPath(file.getName());
     }
@@ -2079,15 +2111,15 @@
                     return null;
                 }
             } else if (tagName.equals(TAG_PERMISSION_GROUP)) {
-                if (parsePermissionGroup(pkg, flags, res, parser, outError) == null) {
+                if (!parsePermissionGroup(pkg, flags, res, parser, outError)) {
                     return null;
                 }
             } else if (tagName.equals(TAG_PERMISSION)) {
-                if (parsePermission(pkg, res, parser, outError) == null) {
+                if (!parsePermission(pkg, res, parser, outError)) {
                     return null;
                 }
             } else if (tagName.equals(TAG_PERMISSION_TREE)) {
-                if (parsePermissionTree(pkg, res, parser, outError) == null) {
+                if (!parsePermissionTree(pkg, res, parser, outError)) {
                     return null;
                 }
             } else if (tagName.equals(TAG_USES_PERMISSION)) {
@@ -2708,22 +2740,44 @@
             }
         }
 
+        final String requiredFeature = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);
+
+        final String requiredNotfeature = sa.getNonConfigurationString(
+                com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0);
+
         sa.recycle();
 
-        if ((maxSdkVersion == 0) || (maxSdkVersion >= Build.VERSION.RESOURCES_SDK_INT)) {
-            if (name != null) {
-                int index = pkg.requestedPermissions.indexOf(name);
-                if (index == -1) {
-                    pkg.requestedPermissions.add(name.intern());
-                } else {
-                    Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
-                            + name + " in package: " + pkg.packageName + " at: "
-                            + parser.getPositionDescription());
-                }
-            }
+        XmlUtils.skipCurrentTag(parser);
+
+        if (name == null) {
+            return true;
         }
 
-        XmlUtils.skipCurrentTag(parser);
+        if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) {
+            return true;
+        }
+
+        // Only allow requesting this permission if the platform supports the given feature.
+        if (requiredFeature != null && mCallback != null && !mCallback.hasFeature(requiredFeature)) {
+            return true;
+        }
+
+        // Only allow requesting this permission if the platform doesn't support the given feature.
+        if (requiredNotfeature != null && mCallback != null
+                && mCallback.hasFeature(requiredNotfeature)) {
+            return true;
+        }
+
+        int index = pkg.requestedPermissions.indexOf(name);
+        if (index == -1) {
+            pkg.requestedPermissions.add(name.intern());
+        } else {
+            Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
+                    + name + " in package: " + pkg.packageName + " at: "
+                    + parser.getPositionDescription());
+        }
+
         return true;
     }
 
@@ -2951,7 +3005,7 @@
         return true;
     }
 
-    private PermissionGroup parsePermissionGroup(Package owner, int flags, Resources res,
+    private boolean parsePermissionGroup(Package owner, int flags, Resources res,
             XmlResourceParser parser, String[] outError)
             throws XmlPullParserException, IOException {
         PermissionGroup perm = new PermissionGroup(owner);
@@ -2968,7 +3022,7 @@
                 com.android.internal.R.styleable.AndroidManifestPermissionGroup_banner)) {
             sa.recycle();
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-            return null;
+            return false;
         }
 
         perm.info.descriptionRes = sa.getResourceId(
@@ -2987,22 +3041,22 @@
         if (!parseAllMetaData(res, parser, "<permission-group>", perm,
                 outError)) {
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-            return null;
+            return false;
         }
 
         owner.permissionGroups.add(perm);
 
-        return perm;
+        return true;
     }
 
-    private Permission parsePermission(Package owner, Resources res,
+    private boolean parsePermission(Package owner, Resources res,
             XmlResourceParser parser, String[] outError)
         throws XmlPullParserException, IOException {
-        Permission perm = new Permission(owner);
 
         TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestPermission);
 
+        Permission perm = new Permission(owner);
         if (!parsePackageItemInfo(owner, perm.info, outError,
                 "<permission>", sa, true /*nameRequired*/,
                 com.android.internal.R.styleable.AndroidManifestPermission_name,
@@ -3013,7 +3067,7 @@
                 com.android.internal.R.styleable.AndroidManifestPermission_banner)) {
             sa.recycle();
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-            return null;
+            return false;
         }
 
         // Note: don't allow this value to be a reference to a resource
@@ -3040,7 +3094,7 @@
         if (perm.info.protectionLevel == -1) {
             outError[0] = "<permission> does not specify protectionLevel";
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-            return null;
+            return false;
         }
 
         perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
@@ -3052,21 +3106,21 @@
                 outError[0] = "<permission>  protectionLevel specifies a non-ephemeral flag but is "
                         + "not based on signature type";
                 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-                return null;
+                return false;
             }
         }
 
         if (!parseAllMetaData(res, parser, "<permission>", perm, outError)) {
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-            return null;
+            return false;
         }
 
         owner.permissions.add(perm);
 
-        return perm;
+        return true;
     }
 
-    private Permission parsePermissionTree(Package owner, Resources res,
+    private boolean parsePermissionTree(Package owner, Resources res,
             XmlResourceParser parser, String[] outError)
         throws XmlPullParserException, IOException {
         Permission perm = new Permission(owner);
@@ -3084,7 +3138,7 @@
                 com.android.internal.R.styleable.AndroidManifestPermissionTree_banner)) {
             sa.recycle();
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-            return null;
+            return false;
         }
 
         sa.recycle();
@@ -3097,7 +3151,7 @@
             outError[0] = "<permission-tree> name has less than three segments: "
                 + perm.info.name;
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-            return null;
+            return false;
         }
 
         perm.info.descriptionRes = 0;
@@ -3107,12 +3161,12 @@
         if (!parseAllMetaData(res, parser, "<permission-tree>", perm,
                 outError)) {
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-            return null;
+            return false;
         }
 
         owner.permissions.add(perm);
 
-        return perm;
+        return true;
     }
 
     private Instrumentation parseInstrumentation(Package owner, Resources res,
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index d3d3c66..5201694 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -96,7 +96,7 @@
     public static final int FLAG_IMMUTABLE = 1 << 8;
 
     /** @hide */
-    public static final int FLAG_MASKABLE_BITMAP = 1 << 9;
+    public static final int FLAG_ADAPTIVE_BITMAP = 1 << 9;
 
     /** @hide */
     public static final int FLAG_CHOOSER = 1 << 10;
@@ -118,7 +118,7 @@
             FLAG_DISABLED,
             FLAG_STRINGS_RESOLVED,
             FLAG_IMMUTABLE,
-            FLAG_MASKABLE_BITMAP,
+            FLAG_ADAPTIVE_BITMAP,
             FLAG_CHOOSER,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -784,7 +784,7 @@
         switch (icon.getType()) {
             case Icon.TYPE_RESOURCE:
             case Icon.TYPE_BITMAP:
-            case Icon.TYPE_BITMAP_MASKABLE:
+            case Icon.TYPE_ADAPTIVE_BITMAP:
                 break; // OK
             default:
                 throw getInvalidIconException();
@@ -917,7 +917,7 @@
          * and will be ignored.
          *
          * <p>Only icons created with {@link Icon#createWithBitmap(Bitmap)},
-         * {@link Icon#createWithMaskableBitmap(Bitmap)}
+         * {@link Icon#createWithAdaptiveBitmap(Bitmap)}
          * and {@link Icon#createWithResource} are supported.
          * Other types, such as URI-based icons, are not supported.
          *
@@ -1615,12 +1615,13 @@
     }
 
     /**
-     * Return whether a shortcut's icon is maskable.
+     * Return whether a shortcut's icon is adaptive bitmap following design guideline
+     * defined in {@link AdaptiveIconDrawable}.
      *
      * @hide internal/unit tests only
      */
-    public boolean hasMaskableBitmap() {
-        return hasFlags(FLAG_MASKABLE_BITMAP);
+    public boolean hasAdaptiveBitmap() {
+        return hasFlags(FLAG_ADAPTIVE_BITMAP);
     }
 
     /**
diff --git a/core/java/android/content/res/CompatResources.java b/core/java/android/content/res/CompatResources.java
index 15575fd..829b6b7 100644
--- a/core/java/android/content/res/CompatResources.java
+++ b/core/java/android/content/res/CompatResources.java
@@ -27,11 +27,17 @@
  */
 public class CompatResources extends Resources {
 
-    private final WeakReference<Context> mContext;
+    private WeakReference<Context> mContext;
 
-    public CompatResources(Resources base, Context context) {
-        super(base.getClassLoader());
-        setImpl(base.getImpl());
+    public CompatResources(ClassLoader cls) {
+        super(cls);
+        mContext = new WeakReference<>(null);
+    }
+
+    /**
+     * @hide
+     */
+    public void setContext(Context context) {
         mContext = new WeakReference<>(context);
     }
 
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index da35ee9..781e235 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -21,6 +21,8 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.DisplayMetrics;
@@ -78,6 +80,11 @@
     private static final int NEEDS_SCREEN_COMPAT = 8;
 
     /**
+     * Set if the application needs to run in with compat resources.
+     */
+    private static final int NEEDS_COMPAT_RES = 16;
+
+    /**
      * The effective screen density we have selected for this application.
      */
     public final int applicationDensity;
@@ -96,6 +103,9 @@
             boolean forceCompat) {
         int compatFlags = 0;
 
+        if (appInfo.targetSdkVersion < VERSION_CODES.O) {
+            compatFlags |= NEEDS_COMPAT_RES;
+        }
         if (appInfo.requiresSmallestWidthDp != 0 || appInfo.compatibleWidthLimitDp != 0
                 || appInfo.largestWidthLimitDp != 0) {
             // New style screen requirements spec.
@@ -274,6 +284,10 @@
         return (mCompatibilityFlags&NEVER_NEEDS_COMPAT) != 0;
     }
 
+    public boolean needsCompatResources() {
+        return (mCompatibilityFlags&NEEDS_COMPAT_RES) != 0;
+    }
+
     /**
      * Returns the translator which translates the coordinates in compatibility mode.
      * @param params the window's parameter
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1259de6..72f51cf 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2913,11 +2913,14 @@
         if (callback == null) {
             throw new IllegalArgumentException("null NetworkCallback");
         }
-        if (need == null && action != REQUEST) {
+        if ((need == null) && (action != REQUEST)) {
             throw new IllegalArgumentException("null NetworkCapabilities");
         }
-        // TODO: throw an exception if callback.networkRequest is not null.
-        // http://b/20701525
+        final int targetSdk = mContext.getApplicationInfo().targetSdkVersion;
+        if ((targetSdk > VERSION_CODES.N_MR1) && (callback.networkRequest != null)) {
+            // http://b/20701525
+            throw new IllegalArgumentException("NetworkCallback already registered");
+        }
         final NetworkRequest request;
         try {
             synchronized(sCallbacks) {
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 817cb5b..0610499 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -275,7 +275,7 @@
          * Magic version number for a current development build, which has
          * not yet turned into an official release.
          */
-        public static final int CUR_DEVELOPMENT = 10000;
+        public static final int CUR_DEVELOPMENT = VMRuntime.SDK_VERSION_CUR_DEVELOPMENT;
 
         /**
          * October 2008: The original, first, version of Android.  Yay!
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index b9e4bad..caea202 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -506,7 +506,7 @@
             }
 
             /**
-             * Detect unbuffered input/output operations.
+             * Disable detection of unbuffered input/output operations.
              */
             public Builder permitUnbufferedIo() {
                 return disable(DETECT_UNBUFFERED_IO);
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 78820b5..b525193 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -35,7 +35,6 @@
     private static final String TAG = "SystemProperties";
     private static final boolean TRACK_KEY_ACCESS = false;
 
-    public static final int PROP_NAME_MAX = 31;
     public static final int PROP_VALUE_MAX = 91;
 
     private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
@@ -82,12 +81,8 @@
     /**
      * Get the value for the given key.
      * @return an empty string if the key isn't found
-     * @throws IllegalArgumentException if the key exceeds 32 characters
      */
     public static String get(String key) {
-        if (key.length() > PROP_NAME_MAX) {
-            throw newKeyTooLargeException(key);
-        }
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key);
     }
@@ -95,12 +90,8 @@
     /**
      * Get the value for the given key.
      * @return if the key isn't found, return def if it isn't null, or an empty string otherwise
-     * @throws IllegalArgumentException if the key exceeds 32 characters
      */
     public static String get(String key, String def) {
-        if (key.length() > PROP_NAME_MAX) {
-            throw newKeyTooLargeException(key);
-        }
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key, def);
     }
@@ -111,12 +102,8 @@
      * @param def a default value to return
      * @return the key parsed as an integer, or def if the key isn't found or
      *         cannot be parsed
-     * @throws IllegalArgumentException if the key exceeds 32 characters
      */
     public static int getInt(String key, int def) {
-        if (key.length() > PROP_NAME_MAX) {
-            throw newKeyTooLargeException(key);
-        }
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_int(key, def);
     }
@@ -127,12 +114,8 @@
      * @param def a default value to return
      * @return the key parsed as a long, or def if the key isn't found or
      *         cannot be parsed
-     * @throws IllegalArgumentException if the key exceeds 32 characters
      */
     public static long getLong(String key, long def) {
-        if (key.length() > PROP_NAME_MAX) {
-            throw newKeyTooLargeException(key);
-        }
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_long(key, def);
     }
@@ -148,25 +131,17 @@
      * @param def a default value to return
      * @return the key parsed as a boolean, or def if the key isn't found or is
      *         not able to be parsed as a boolean.
-     * @throws IllegalArgumentException if the key exceeds 32 characters
      */
     public static boolean getBoolean(String key, boolean def) {
-        if (key.length() > PROP_NAME_MAX) {
-            throw newKeyTooLargeException(key);
-        }
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_boolean(key, def);
     }
 
     /**
      * Set the value for the given key.
-     * @throws IllegalArgumentException if the key exceeds 32 characters
      * @throws IllegalArgumentException if the value exceeds 92 characters
      */
     public static void set(String key, String val) {
-        if (key.length() > PROP_NAME_MAX) {
-            throw newKeyTooLargeException(key);
-        }
         if (val != null && val.length() > PROP_VALUE_MAX) {
             throw newValueTooLargeException(key, val);
         }
@@ -196,11 +171,6 @@
         }
     }
 
-    private static IllegalArgumentException newKeyTooLargeException(String key) {
-        return new IllegalArgumentException("system property key '" + key + "' is longer than "
-                + PROP_NAME_MAX + " characters");
-    }
-
     private static IllegalArgumentException newValueTooLargeException(String key, String value) {
         return new IllegalArgumentException("value of system property '" + key + "' is longer than "
                 + PROP_VALUE_MAX + " characters: " + value);
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index bf03cce..8549cff 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -26,7 +26,18 @@
 /**
  * UpdateEngine handles calls to the update engine which takes care of A/B OTA
  * updates. It wraps up the update engine Binder APIs and exposes them as
- * SystemApis, which will be called by system apps like GmsCore.
+ * SystemApis, which will be called by the system app responsible for OTAs.
+ * On a Google device, this will be GmsCore.
+ *
+ * The minimal flow is:
+ * <ol>
+ * <li>Create a new UpdateEngine instance.
+ * <li>Call {@link #bind}, optionally providing callbacks.
+ * <li>Call {@link #applyPayload}.
+ * </ol>
+ *
+ * In addition, methods are provided to {@link #cancel} or
+ * {@link #suspend}/{@link #resume} application of an update.
  *
  * The APIs defined in this class and UpdateEngineCallback class must be in
  * sync with the ones in
@@ -80,12 +91,20 @@
 
     private IUpdateEngine mUpdateEngine;
 
+    /**
+     * Creates a new instance.
+     */
     @SystemApi
     public UpdateEngine() {
         mUpdateEngine = IUpdateEngine.Stub.asInterface(
                 ServiceManager.getService(UPDATE_ENGINE_SERVICE));
     }
 
+    /**
+     * Prepares this instance for use. The callback will be notified on any
+     * status change, and when the update completes. A handler can be supplied
+     * to control which thread runs the callback, or null.
+     */
     @SystemApi
     public boolean bind(final UpdateEngineCallback callback, final Handler handler) {
         IUpdateEngineCallback updateEngineCallback = new IUpdateEngineCallback.Stub() {
@@ -125,11 +144,42 @@
         }
     }
 
+    /**
+     * Equivalent to {@code bind(callback, null)}.
+     */
     @SystemApi
     public boolean bind(final UpdateEngineCallback callback) {
         return bind(callback, null);
     }
 
+    /**
+     * Applies the payload found at the given {@code url}. For non-streaming
+     * updates, the URL can be a local file using the {@code file://} scheme.
+     *
+     * <p>The {@code offset} and {@code size} parameters specify the location
+     * of the payload within the file represented by the URL. This is useful
+     * if the downloadable package at the URL contains more than just the
+     * update_engine payload (such as extra metadata). This is true for
+     * Google's OTA system, where the URL points to a zip file in which the
+     * payload is stored uncompressed within the zip file alongside other
+     * data.
+     *
+     * <p>The {@code headerKeyValuePairs} parameter is used to pass metadata
+     * to update_engine. In Google's implementation, this is stored as
+     * {@code payload_properties.txt} in the zip file. It's generated by the
+     * script {@code system/update_engine/scripts/brillo_update_payload}.
+     * The complete list of keys and their documentation is in
+     * {@code system/update_engine/common/constants.cc}, but an example
+     * might be:
+     * <pre>
+     * String[] pairs = {
+     *   "FILE_HASH=lURPCIkIAjtMOyB/EjQcl8zDzqtD6Ta3tJef6G/+z2k=",
+     *   "FILE_SIZE=871903868",
+     *   "METADATA_HASH=tBvj43QOB0Jn++JojcpVdbRLz0qdAuL+uTkSy7hokaw=",
+     *   "METADATA_SIZE=70604"
+     * };
+     * </pre>
+     */
     @SystemApi
     public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) {
         try {
@@ -139,6 +189,15 @@
         }
     }
 
+    /**
+     * Permanently cancels an in-progress update.
+     *
+     * <p>See {@link #resetStatus} to undo a finshed update (only available
+     * before the updated system has been rebooted).
+     *
+     * <p>See {@link #suspend} for a way to temporarily stop an in-progress
+     * update with the ability to resume it later.
+     */
     @SystemApi
     public void cancel() {
         try {
@@ -148,6 +207,10 @@
         }
     }
 
+    /**
+     * Suspends an in-progress update. This can be undone by calling
+     * {@link #resume}.
+     */
     @SystemApi
     public void suspend() {
         try {
@@ -157,6 +220,9 @@
         }
     }
 
+    /**
+     * Resumes a suspended update.
+     */
     @SystemApi
     public void resume() {
         try {
@@ -166,6 +232,15 @@
         }
     }
 
+    /**
+     * Resets the bootable flag on the non-current partition and all internal
+     * update_engine state. This can be used after an unwanted payload has been
+     * successfully applied and the device has not yet been rebooted to signal
+     * that we no longer want to boot into that updated system. After this call
+     * completes, update_engine will no longer report
+     * {@code UPDATED_NEED_REBOOT}, so your callback can remove any outstanding
+     * notification that rebooting into the new system is possible.
+     */
     @SystemApi
     public void resetStatus() {
         try {
diff --git a/core/java/android/os/UpdateEngineCallback.java b/core/java/android/os/UpdateEngineCallback.java
index b3b856f..afff60a 100644
--- a/core/java/android/os/UpdateEngineCallback.java
+++ b/core/java/android/os/UpdateEngineCallback.java
@@ -19,7 +19,8 @@
 import android.annotation.SystemApi;
 
 /**
- * Callback function for UpdateEngine.
+ * Callback function for UpdateEngine. Used to keep the caller up to date
+ * with progress, so the UI (if any) can be updated.
  *
  * The APIs defined in this class and UpdateEngine class must be in sync with
  * the ones in
@@ -31,9 +32,19 @@
 @SystemApi
 public abstract class UpdateEngineCallback {
 
+    /**
+     * Invoked when anything changes. The value of {@code status} will
+     * be one of the values from {@link UpdateEngine.UpdateStatusConstants},
+     * and {@code percent} will be valid [TODO: in which cases?].
+     */
     @SystemApi
     public abstract void onStatusUpdate(int status, float percent);
 
+    /**
+     * Invoked when the payload has been applied, whether successfully or
+     * unsuccessfully. The value of {@code errorCode} will be one of the
+     * values from {@link UpdateEngine.ErrorCodeConstants}.
+     */
     @SystemApi
     public abstract void onPayloadApplicationComplete(int errorCode);
 }
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 7b63b62..a21e05e 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -435,7 +435,7 @@
             return null;
         }
 
-        final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE);
+        final Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
         intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM);
 
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 089eaec..62c5dca 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -410,11 +410,13 @@
     /**
      * Sets a {@link PreferenceDataStore} to be used by this Preference instead of using
      * {@link android.content.SharedPreferences}.
-     * <p>
-     * The data store will remain assigned even if the Preference is moved between multiple
-     * instances of {@link PreferenceFragment}.
+     *
+     * <p>The data store will remain assigned even if the Preference is moved around the preference
+     * hierarchy. It will also override a data store propagated from the {@link PreferenceManager}
+     * that owns this Preference.
      *
      * @param dataStore The {@link PreferenceDataStore} to be used by this Preference.
+     * @see PreferenceManager#setPreferenceDataStore(PreferenceDataStore)
      */
     public void setPreferenceDataStore(PreferenceDataStore dataStore) {
         mPreferenceDataStore = dataStore;
@@ -424,6 +426,12 @@
      * Returns {@link PreferenceDataStore} used by this Preference. Returns {@code null} if
      * {@link android.content.SharedPreferences} is used instead.
      *
+     * <p>By default preferences always use {@link android.content.SharedPreferences}. To make this
+     * preference to use the {@link PreferenceDataStore} you need to assign your implementation
+     * to the Preference itself via {@link #setPreferenceDataStore(PreferenceDataStore)} or to its
+     * {@link PreferenceManager} via
+     * {@link PreferenceManager#setPreferenceDataStore(PreferenceDataStore)}.
+     *
      * @return The {@link PreferenceDataStore} used by this Preference or {@code null} if none.
      */
     @Nullable
@@ -1457,12 +1465,12 @@
     }
 
     /**
-     * Implement this to set the initial value of the Preference. 
+     * Implement this to set the initial value of the Preference.
      * <p>
-     * If <var>restorePersistedValue</var> is true, you should restore the 
-     * Preference value from the {@link android.content.SharedPreferences}. If 
-     * <var>restorePersistedValue</var> is false, you should set the Preference 
-     * value to defaultValue that is given (and possibly store to SharedPreferences 
+     * If <var>restorePersistedValue</var> is true, you should restore the
+     * Preference value from the {@link android.content.SharedPreferences}. If
+     * <var>restorePersistedValue</var> is false, you should set the Preference
+     * value to defaultValue that is given (and possibly store to SharedPreferences
      * if {@link #shouldPersist()} is true).
      * <p>
      * This may not always be called. One example is if it should not persist
diff --git a/core/java/android/preference/PreferenceDataStore.java b/core/java/android/preference/PreferenceDataStore.java
index e1a08ac..8caa404 100644
--- a/core/java/android/preference/PreferenceDataStore.java
+++ b/core/java/android/preference/PreferenceDataStore.java
@@ -21,16 +21,32 @@
 import java.util.Set;
 
 /**
- * A data store interface to be implemented and provided to the Preferences framework.
+ * A data store interface to be implemented and provided to the Preferences framework. This can be
+ * used to replace the default {@link android.content.SharedPreferences}, if needed.
  *
- * Use this to replace the default {@link android.content.SharedPreferences}. By default, all "put"
- * methods throw {@link UnsupportedOperationException}.
+ * <p>In most cases you want to use {@link android.content.SharedPreferences} as it is automatically
+ * backed up and migrated to new devices. However, providing custom data store to preferences can be
+ * useful if your app stores its preferences in a local db, cloud or they are device specific like
+ * "Developer settings". It might be also useful when you want to use the preferences UI but
+ * the data are not supposed to be stored at all because they are valid per session only.
+ *
+ * <p>Once a put method is called it is full responsibility of the data store implementation to
+ * safely store the given values. Time expensive operations need to be done in the background to
+ * prevent from blocking the UI. You also need to have a plan on how to serialize the data in case
+ * the activity holding this object gets destroyed.
+ *
+ * <p>By default, all "put" methods throw {@link UnsupportedOperationException}.
+ *
+ * @see Preference#setPreferenceDataStore(PreferenceDataStore)
+ * @see PreferenceManager#setPreferenceDataStore(PreferenceDataStore)
  */
 public interface PreferenceDataStore {
 
     /**
      * Set a String value to the data store.
      *
+     * <p>Once the value is set the data store is responsible for holding it.
+     *
      * @param key The name of the preference to modify.
      * @param value The new value for the preference.
      * @see #getString(String, String)
@@ -42,6 +58,8 @@
     /**
      * Set a set of String value to the data store.
      *
+     * <p>Once the value is set the data store is responsible for holding it.
+     *
      * @param key The name of the preference to modify.
      * @param values The set of new values for the preference.
      * @see #getStringSet(String, Set)
@@ -53,6 +71,8 @@
     /**
      * Set an int value to the data store.
      *
+     * <p>Once the value is set the data store is responsible for holding it.
+     *
      * @param key The name of the preference to modify.
      * @param value The new value for the preference.
      * @see #getInt(String, int)
@@ -64,6 +84,8 @@
     /**
      * Set a long value to the data store.
      *
+     * <p>Once the value is set the data store is responsible for holding it.
+     *
      * @param key The name of the preference to modify.
      * @param value The new value for the preference.
      * @see #getLong(String, long)
@@ -75,6 +97,8 @@
     /**
      * Set a float value to the data store.
      *
+     * <p>Once the value is set the data store is responsible for holding it.
+     *
      * @param key The name of the preference to modify.
      * @param value The new value for the preference.
      * @see #getFloat(String, float)
@@ -86,6 +110,8 @@
     /**
      * Set a boolean value to the data store.
      *
+     * <p>Once the value is set the data store is responsible for holding it.
+     *
      * @param key The name of the preference to modify.
      * @param value The new value for the preference.
      * @see #getBoolean(String, boolean)
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index 756c3f4..2570374 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -208,10 +208,13 @@
 
     /**
      * Sets a {@link PreferenceDataStore} to be used by all Preferences associated with this manager
-     * that don't have a custom {@link PreferenceDataStore} assigned. Also if the data store is set,
-     * the Preferences will no longer use {@link android.content.SharedPreferences}.
+     * that don't have a custom {@link PreferenceDataStore} assigned via
+     * {@link Preference#setPreferenceDataStore(PreferenceDataStore)}. Also if the data store is
+     * set, the child preferences won't use {@link android.content.SharedPreferences} as long as
+     * they are assigned to this manager.
      *
      * @param dataStore The {@link PreferenceDataStore} to be used by this manager.
+     * @see Preference#setPreferenceDataStore(PreferenceDataStore)
      */
     public void setPreferenceDataStore(PreferenceDataStore dataStore) {
         mPreferenceDataStore = dataStore;
@@ -219,9 +222,10 @@
 
     /**
      * Returns the {@link PreferenceDataStore} associated with this manager or {@code null} if
-     * {@link android.content.SharedPreferences} are used instead.
+     * the default {@link android.content.SharedPreferences} are used instead.
      *
      * @return The {@link PreferenceDataStore} associated with this manager or {@code null} if none.
+     * @see #setPreferenceDataStore(PreferenceDataStore)
      */
     @Nullable
     public PreferenceDataStore getPreferenceDataStore() {
@@ -358,8 +362,11 @@
      * Sets the name of the SharedPreferences file that preferences managed by this
      * will use.
      *
+     * <p>If custom {@link PreferenceDataStore} is set, this won't override its usage.
+     *
      * @param sharedPreferencesName The name of the SharedPreferences file.
      * @see Context#getSharedPreferences(String, int)
+     * @see #setPreferenceDataStore(PreferenceDataStore)
      */
     public void setSharedPreferencesName(String sharedPreferencesName) {
         mSharedPreferencesName = sharedPreferencesName;
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 5486162..33b5903 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -119,6 +119,9 @@
      * <p>Location should specify a document URI or a tree URI with document ID. If
      * this URI identifies a non-directory, document navigator will attempt to use the parent
      * of the document as the initial location.
+     *
+     * <p>The initial location is system specific if this extra is missing or document navigator
+     * failed to locate the desired initial location.
      */
     public static final String EXTRA_INITIAL_URI = "android.provider.extra.INITIAL_URI";
 
@@ -162,9 +165,6 @@
     public static final String ACTION_MANAGE_DOCUMENT = "android.provider.action.MANAGE_DOCUMENT";
 
     /** {@hide} */
-    public static final String ACTION_BROWSE = "android.provider.action.BROWSE";
-
-    /** {@hide} */
     public static final String
             ACTION_DOCUMENT_ROOT_SETTINGS = "android.provider.action.DOCUMENT_ROOT_SETTINGS";
 
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 5461e6b..ad8ad69 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -138,8 +138,6 @@
     public static final int KM_PURPOSE_DECRYPT = 1;
     public static final int KM_PURPOSE_SIGN = 2;
     public static final int KM_PURPOSE_VERIFY = 3;
-    public static final int KM_PURPOSE_DERIVE_KEY = 4;
-    public static final int KM_PURPOSE_WRAP_KEY = 5;
 
     // Key formats.
     public static final int KM_KEY_FORMAT_X509 = 0;
diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java
index 4099f59..3c211eb 100644
--- a/core/java/android/service/autofill/AutoFillService.java
+++ b/core/java/android/service/autofill/AutoFillService.java
@@ -15,9 +15,12 @@
  */
 package android.service.autofill;
 
+import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Message;
 import android.os.RemoteException;
+import android.view.accessibility.AccessibilityInteractionClient;
 import com.android.internal.os.HandlerCaller;
 import android.annotation.SdkConstant;
 import android.app.Activity;
@@ -79,6 +82,15 @@
 
     private final IAutoFillService mInterface = new IAutoFillService.Stub() {
         @Override
+        public void onInit(IAutoFillServiceConnection connection) {
+            if (connection != null) {
+                mHandlerCaller.obtainMessageO(MSG_CONNECT, connection).sendToTarget();
+            } else {
+                mHandlerCaller.obtainMessage(MSG_DISCONNECT).sendToTarget();
+            }
+        }
+
+        @Override
         public void onFillRequest(AssistStructure structure, Bundle extras,
                 IFillCallback callback) {
             ICancellationSignal transport = CancellationSignal.createTransport();
@@ -98,21 +110,12 @@
             mHandlerCaller.obtainMessageOOO(MSG_ON_SAVE_REQUEST, structure,
                     extras, callback).sendToTarget();
         }
-
-        @Override
-        public void onConnected() {
-            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CONNECT));
-        }
-
-        @Override
-        public void onDisconnected() {
-            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DISCONNECT));
-        }
     };
 
     private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
         switch (msg.what) {
             case MSG_CONNECT: {
+                mConnection = (IAutoFillServiceConnection) msg.obj;
                 onConnected();
                 break;
             } case MSG_ON_FILL_REQUEST: {
@@ -136,6 +139,7 @@
                 break;
             } case MSG_DISCONNECT: {
                 onDisconnected();
+                mConnection = null;
                 break;
             } default: {
                 Log.w(TAG, "MyCallbacks received invalid message type: " + msg);
@@ -145,6 +149,8 @@
 
     private HandlerCaller mHandlerCaller;
 
+    private IAutoFillServiceConnection mConnection;
+
     /**
      * {@inheritDoc}
      *
@@ -223,4 +229,22 @@
     public void onDisconnected() {
         //TODO(b/33197203): is not called anymore, fix it!
     }
+
+    /**
+     * Disables the service. After calling this method, the service will
+     * be disabled and settings will show that it is turned off.
+     *
+     * <p>You should call this method only after a call to {@link #onConnected()}
+     * and before the corresponding call to {@link #onDisconnected()}. In other words
+     * you can disable your service only while the system is connected to it.</p>
+     */
+    public final void disableSelf() {
+        if (mConnection != null) {
+            try {
+                mConnection.disableSelf();
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+    }
 }
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index ba75c8b..ef551ad 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -171,7 +171,7 @@
 
         if (false) {
             // TODO(b/33197203, 35727295): this is how mSaveInfo will be set once we don't support
-            // FillResponse.setSavableIds()
+            // FillResponse.addSavableIds()
             mSaveInfo = builder.mSaveInfo;
             if (mSaveInfo != null) {
                 mSaveInfo.addSavableIds(mDatasets);
@@ -181,11 +181,11 @@
                 }
             }
         } else {
-            // Temporary workaround to support FillResponse.setSavableIds()
+            // Temporary workaround to support FillResponse.addSavableIds()
             SaveInfo saveInfo = builder.mSaveInfoBuilder != null ? builder.mSaveInfoBuilder.build()
                     : builder.mSaveInfo;
 
-            // Handle the the case where service didn't call setSavableIds() because it would
+            // Handle the the case where service didn't call addSavableIds() because it would
             // contain just the ids from the datasets.
             if (saveInfo == null && mDatasets != null) {
                 saveInfo = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC).build();
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index fa1ea65..80685d8 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -18,6 +18,7 @@
 
 import android.app.assist.AssistStructure;
 import android.os.Bundle;
+import android.service.autofill.IAutoFillServiceConnection;
 import android.service.autofill.IFillCallback;
 import android.service.autofill.ISaveCallback;
 import com.android.internal.os.IResultReceiver;
@@ -28,10 +29,9 @@
  * @hide
  */
 oneway interface IAutoFillService {
+    void onInit(in IAutoFillServiceConnection connection);
     void onFillRequest(in AssistStructure structure, in Bundle extras,
             in IFillCallback callback);
     void onSaveRequest(in AssistStructure structure, in Bundle extras,
             in ISaveCallback callback);
-    void onConnected();
-    void onDisconnected();
 }
diff --git a/core/java/android/service/autofill/IAutoFillServiceConnection.aidl b/core/java/android/service/autofill/IAutoFillServiceConnection.aidl
new file mode 100644
index 0000000..cf73a10
--- /dev/null
+++ b/core/java/android/service/autofill/IAutoFillServiceConnection.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.service.autofill;
+
+/**
+ * Interface from an auto fill service to the system.
+ *
+ * @hide
+ */
+interface IAutoFillServiceConnection {
+    void disableSelf();
+}
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 096f28b..a8f9aee 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.IntentSender;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -72,6 +73,8 @@
     public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3;
 
     private final @SaveDataType int mType;
+    private CharSequence mNegativeActionTitle;
+    private IntentSender mNegativeActionListener;
     private ArraySet<AutoFillId> mSavableIds;
     private final CharSequence mDescription;
 
@@ -88,11 +91,23 @@
 
     private SaveInfo(Builder builder) {
         mType = builder.mType;
+        mNegativeActionTitle = builder.mNegativeActionTitle;
+        mNegativeActionListener = builder.mNegativeActionListener;
         mSavableIds = builder.mSavableIds;
         mDescription = builder.mDescription;
     }
 
     /** @hide */
+    public @Nullable CharSequence getNegativeActionTitle() {
+        return mNegativeActionTitle;
+    }
+
+    /** @hide */
+    public @Nullable IntentSender getNegativeActionListener() {
+        return mNegativeActionListener;
+    }
+
+    /** @hide */
     public @Nullable ArraySet<AutoFillId> getSavableIds() {
         return mSavableIds;
     }
@@ -132,6 +147,8 @@
     public static final class Builder {
 
         private final @SaveDataType int mType;
+        private CharSequence mNegativeActionTitle;
+        private IntentSender mNegativeActionListener;
         private ArraySet<AutoFillId> mSavableIds;
         private CharSequence mDescription;
         private boolean mDestroyed;
@@ -195,6 +212,42 @@
         }
 
         /**
+         * Sets the title and listener for the negative save action.
+         *
+         * <p>This allows a fill-provider to customize the text and be
+         * notified when the user selects the negative action in the save
+         * UI. Note that selecting the negative action regardless of its text
+         * and listener being customized would dismiss the save UI and if a
+         * custom listener intent is provided then this intent will be
+         * started.</p>
+         *
+         * <p>This customization could be useful for providing additional
+         * semantics to the negative action. For example, a fill-provider
+         * can use this mechanism to add a "Disable" function or a "More info"
+         * function, etc. Note that the save action is exclusively controlled
+         * by the platform to ensure user consent is collected to release
+         * data from the filled app to the fill-provider.</p>
+         *
+         * @param title The action title.
+         * @param listener The action listener.
+         * @return This builder.
+         *
+         * @throws IllegalArgumentException If the title and the listener
+         *     are not both either null or non-null.
+         */
+        public @NonNull Builder setNegativeAction(@Nullable CharSequence title,
+                @Nullable IntentSender listener) {
+            throwIfDestroyed();
+            if (title == null ^ listener == null) {
+                throw new IllegalArgumentException("title and listener"
+                        + " must be both non-null or null");
+            }
+            mNegativeActionTitle = title;
+            mNegativeActionListener = listener;
+            return this;
+        }
+
+        /**
          * Builds a new {@link SaveInfo} instance.
          */
         public SaveInfo build() {
@@ -235,6 +288,8 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(mType);
+        parcel.writeCharSequence(mNegativeActionTitle);
+        parcel.writeParcelable(mNegativeActionListener, flags);
         parcel.writeTypedArraySet(mSavableIds, flags);
         parcel.writeCharSequence(mDescription);
     }
@@ -246,6 +301,7 @@
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
             final Builder builder = new Builder(parcel.readInt());
+            builder.setNegativeAction(parcel.readCharSequence(), parcel.readParcelable(null));
             final ArraySet<AutoFillId> savableIds = parcel.readTypedArraySet(null);
             final int savableIdsCount = (savableIds != null) ? savableIds.size() : 0;
             for (int i = 0; i < savableIdsCount; i++) {
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index 1781c2a..2116847 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -126,7 +126,9 @@
     public static final String EXTRA_TOKEN = "token";
 
     /**
-     * @hide
+     * May be included when {@link #ACTION_QS_TILE_PREFERENCES} is launched from a tile.
+     * This extra may contain the {@link ComponentName} of the tile that triggered
+     * the preferences to be opened.
      */
     public static final String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT";
 
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index c3d3f39..16d4666 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -94,6 +94,7 @@
             }
         }
     };
+    private boolean mAcceptAllTouches;
 
     public NotificationHeaderView(Context context) {
         this(context, null);
@@ -374,6 +375,8 @@
                 case MotionEvent.ACTION_DOWN:
                     mTrackGesture = false;
                     if (isInside(x, y)) {
+                        mDownX = x;
+                        mDownY = y;
                         mTrackGesture = true;
                         return true;
                     }
@@ -396,11 +399,12 @@
         }
 
         private boolean isInside(float x, float y) {
+            if (mAcceptAllTouches) {
+                return true;
+            }
             for (int i = 0; i < mTouchRects.size(); i++) {
                 Rect r = mTouchRects.get(i);
                 if (r.contains((int) x, (int) y)) {
-                    mDownX = x;
-                    mDownY = y;
                     return true;
                 }
             }
@@ -433,4 +437,9 @@
         }
         return mTouchListener.isInside(x, y);
     }
+
+    @RemotableViewMethod
+    public void setAcceptAllTouches(boolean acceptAllTouches) {
+        mAcceptAllTouches = acceptAllTouches;
+    }
 }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0ac16c1..b718696 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -95,11 +95,6 @@
             IBinder displayToken, int mode);
     private static native void nativeDeferTransactionUntil(long nativeObject,
             IBinder handle, long frame);
-    private static native void nativeDeferTransactionUntilSurface(long nativeObject,
-            long surfaceObject, long frame);
-    private static native void nativeReparentChildren(long nativeObject,
-            IBinder handle);
-    private static native void nativeSeverChildren(long nativeObject);
     private static native void nativeSetOverrideScalingMode(long nativeObject,
             int scalingMode);
     private static native IBinder nativeGetHandle(long nativeObject);
@@ -426,18 +421,6 @@
         nativeDeferTransactionUntil(mNativeObject, handle, frame);
     }
 
-    public void deferTransactionUntil(Surface barrier, long frame) {
-        nativeDeferTransactionUntilSurface(mNativeObject, barrier.mNativeObject, frame);
-    }
-
-    public void reparentChildren(IBinder newParentHandle) {
-        nativeReparentChildren(mNativeObject, newParentHandle);
-    }
-
-    public void detachChildren() {
-        nativeSeverChildren(mNativeObject);
-    }
-
     public void setOverrideScalingMode(int scalingMode) {
         checkNotReleased();
         nativeSetOverrideScalingMode(mNativeObject, scalingMode);
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index b5912bc..3cf5af4 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -27,7 +27,6 @@
     private long mNativeClient; // SurfaceComposerClient*
 
     private static native long nativeCreate();
-    private static native long nativeCreateScoped(long surfacePtr);
     private static native void nativeDestroy(long ptr);
     private static native void nativeKill(long ptr);
 
@@ -36,10 +35,6 @@
         mNativeClient = nativeCreate();
     }
 
-    public SurfaceSession(Surface root) {
-        mNativeClient = nativeCreateScoped(root.mNativeObject);
-    }
-
     /* no user serviceable parts here ... */
     @Override
     protected void finalize() throws Throwable {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 6430633..d2577d4 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -16,10 +16,6 @@
 
 package android.view;
 
-import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_SUBLAYER;
-import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_OVERLAY_SUBLAYER;
-import static android.view.WindowManagerPolicy.APPLICATION_PANEL_SUBLAYER;
-
 import android.content.Context;
 import android.content.res.CompatibilityInfo.Translator;
 import android.content.res.Configuration;
@@ -30,12 +26,16 @@
 import android.graphics.Region;
 import android.os.Handler;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
 
+import com.android.internal.view.BaseIWindow;
 import com.android.internal.view.SurfaceCallbackHelper;
 
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -92,8 +92,8 @@
  * positioned asynchronously.</p>
  */
 public class SurfaceView extends View {
-    private static final String TAG = "SurfaceView";
-    private static final boolean DEBUG = false;
+    static private final String TAG = "SurfaceView";
+    static private final boolean DEBUG = false;
 
     final ArrayList<SurfaceHolder.Callback> mCallbacks
             = new ArrayList<SurfaceHolder.Callback>();
@@ -102,23 +102,28 @@
 
     final ReentrantLock mSurfaceLock = new ReentrantLock();
     final Surface mSurface = new Surface();       // Current surface in use
+    final Surface mNewSurface = new Surface();    // New surface we are switching to
     boolean mDrawingStopped = true;
-    // We use this to track if the application has produced a frame
-    // in to the Surface. Up until that point, we should be careful not to punch
-    // holes.
-    boolean mDrawFinished = false;
 
-    final Rect mScreenRect = new Rect();
-    SurfaceSession mSurfaceSession;
-
-    SurfaceControl mSurfaceControl;
+    final WindowManager.LayoutParams mLayout
+            = new WindowManager.LayoutParams();
+    IWindowSession mSession;
+    MyWindow mWindow;
+    final Rect mVisibleInsets = new Rect();
+    final Rect mWinFrame = new Rect();
+    final Rect mOverscanInsets = new Rect();
+    final Rect mContentInsets = new Rect();
+    final Rect mStableInsets = new Rect();
+    final Rect mOutsets = new Rect();
+    final Rect mBackdropFrame = new Rect();
     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;
+    static final int GET_NEW_SURFACE_MSG = 2;
+    static final int UPDATE_WINDOW_MSG = 3;
 
-    int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+    int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
 
     boolean mIsCreating = false;
     private volatile boolean mRtHandlingPositionUpdates = false;
@@ -130,9 +135,11 @@
                 case KEEP_SCREEN_ON_MSG: {
                     setKeepScreenOn(msg.arg1 != 0);
                 } break;
-                case DRAW_FINISHED_MSG: {
-                    mDrawFinished = true;
-                    invalidate();
+                case GET_NEW_SURFACE_MSG: {
+                    handleGetNewSurface();
+                } break;
+                case UPDATE_WINDOW_MSG: {
+                    updateWindow();
                 } break;
             }
         }
@@ -142,7 +149,7 @@
             = new ViewTreeObserver.OnScrollChangedListener() {
                     @Override
                     public void onScrollChanged() {
-                        updateSurface();
+                        updateWindow();
                     }
             };
 
@@ -152,14 +159,13 @@
                 public boolean onPreDraw() {
                     // reposition ourselves where the surface is
                     mHaveFrame = getWidth() > 0 && getHeight() > 0;
-                    updateSurface();
+                    updateWindow();
                     return true;
                 }
             };
 
     boolean mRequestedVisible = false;
     boolean mWindowVisibility = false;
-    boolean mLastWindowVisibility = false;
     boolean mViewVisibility = false;
     int mRequestedWidth = -1;
     int mRequestedHeight = -1;
@@ -175,17 +181,19 @@
     boolean mVisible = false;
     int mWindowSpaceLeft = -1;
     int mWindowSpaceTop = -1;
-    int mSurfaceWidth = -1;
-    int mSurfaceHeight = -1;
+    int mWindowSpaceWidth = -1;
+    int mWindowSpaceHeight = -1;
     int mFormat = -1;
     final Rect mSurfaceFrame = new Rect();
     int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
+    boolean mUpdateWindowNeeded;
+    boolean mReportDrawNeeded;
     private Translator mTranslator;
+    private int mWindowInsetLeft;
+    private int mWindowInsetTop;
 
     private boolean mGlobalListenersAdded;
 
-    private int mSurfaceFlags = SurfaceControl.HIDDEN;
-
     public SurfaceView(Context context) {
         this(context, null);
     }
@@ -219,8 +227,11 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mParent.requestTransparentRegion(this);
+        mSession = getWindowSession();
+        mLayout.token = getWindowToken();
+        mLayout.setTitle("SurfaceView - " + getViewRootImpl().getTitle());
+        mLayout.packageName = mContext.getOpPackageName();
         mViewVisibility = getVisibility() == VISIBLE;
-        mRequestedVisible = mViewVisibility && mWindowVisibility;
 
         if (!mGlobalListenersAdded) {
             ViewTreeObserver observer = getViewTreeObserver();
@@ -235,7 +246,7 @@
         super.onWindowVisibilityChanged(visibility);
         mWindowVisibility = visibility == VISIBLE;
         mRequestedVisible = mWindowVisibility && mViewVisibility;
-        updateSurface();
+        updateWindow();
     }
 
     @Override
@@ -253,7 +264,7 @@
             requestLayout();
         }
         mRequestedVisible = newRequestedVisible;
-        updateSurface();
+        updateWindow();
     }
 
     @Override
@@ -266,14 +277,19 @@
         }
 
         mRequestedVisible = false;
-
-        updateSurface();
-        if (mSurfaceControl != null) {
-            mSurfaceControl.destroy();
-        }
-        mSurfaceControl = null;
-
+        updateWindow();
         mHaveFrame = false;
+        if (mWindow != null) {
+            try {
+                mSession.remove(mWindow);
+            } catch (RemoteException ex) {
+                // Not much we can do here...
+            }
+            mWindow = null;
+        }
+        mSession = null;
+        mLayout.token = null;
+
         super.onDetachedFromWindow();
     }
 
@@ -292,13 +308,13 @@
     @Override
     protected boolean setFrame(int left, int top, int right, int bottom) {
         boolean result = super.setFrame(left, top, right, bottom);
-        updateSurface();
+        updateWindow();
         return result;
     }
 
     @Override
     public boolean gatherTransparentRegion(Region region) {
-        if (isAboveParent()) {
+        if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
             return super.gatherTransparentRegion(region);
         }
 
@@ -325,7 +341,7 @@
 
     @Override
     public void draw(Canvas canvas) {
-        if (mDrawFinished && !isAboveParent()) {
+        if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
             // draw() is not called when SKIP_DRAW is set
             if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
                 // punch a whole in the view-hierarchy below us
@@ -337,8 +353,8 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        if (mDrawFinished && !isAboveParent()) {
-            // draw() is not called when SKIP_DRAW is set
+        if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
+            // if SKIP_DRAW is cleared, draw() has already punched a hole
             if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                 // punch a whole in the view-hierarchy below us
                 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
@@ -359,8 +375,9 @@
      * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
      */
     public void setZOrderMediaOverlay(boolean isMediaOverlay) {
-        mSubLayer = isMediaOverlay
-            ? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER;
+        mWindowType = isMediaOverlay
+                ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
+                : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
     }
 
     /**
@@ -378,9 +395,12 @@
      */
     public void setZOrderOnTop(boolean onTop) {
         if (onTop) {
-            mSubLayer = APPLICATION_PANEL_SUBLAYER;
+            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+            // ensures the surface is placed below the IME
+            mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         } else {
-            mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
+            mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         }
     }
 
@@ -398,23 +418,31 @@
      */
     public void setSecure(boolean isSecure) {
         if (isSecure) {
-            mSurfaceFlags |= SurfaceControl.SECURE;
+            mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE;
         } else {
-            mSurfaceFlags &= ~SurfaceControl.SECURE;
+            mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE;
         }
     }
 
+    /**
+     * Hack to allow special layering of windows.  The type is one of the
+     * types in WindowManager.LayoutParams.  This is a hack so:
+     * @hide
+     */
+    public void setWindowType(int type) {
+        mWindowType = type;
+    }
+
     /** @hide */
-    protected void updateSurface() {
+    protected void updateWindow() {
         if (!mHaveFrame) {
             return;
         }
         ViewRootImpl viewRoot = getViewRootImpl();
-        if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
-            return;
+        if (viewRoot != null) {
+            mTranslator = viewRoot.mTranslator;
         }
 
-        mTranslator = viewRoot.mTranslator;
         if (mTranslator != null) {
             mSurface.setCompatibilityTranslator(mTranslator);
         }
@@ -424,15 +452,17 @@
         int myHeight = mRequestedHeight;
         if (myHeight <= 0) myHeight = getHeight();
 
+        final boolean creating = mWindow == null;
         final boolean formatChanged = mFormat != mRequestedFormat;
-        final boolean creating = (mSurfaceControl == null || formatChanged)
-                && mRequestedVisible;
-        final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
+        final boolean sizeChanged = mWindowSpaceWidth != myWidth || mWindowSpaceHeight != myHeight;
         final boolean visibleChanged = mVisible != mRequestedVisible;
-        final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
+        final boolean layoutSizeChanged = getWidth() != mLayout.width
+                || getHeight() != mLayout.height;
+
         boolean redrawNeeded = false;
 
-        if (creating || formatChanged || sizeChanged || visibleChanged || windowVisibleChanged) {
+        if (creating || formatChanged || sizeChanged || visibleChanged
+            || mUpdateWindowNeeded || mReportDrawNeeded) {
             getLocationInWindow(mLocation);
 
             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
@@ -446,74 +476,93 @@
                 final boolean visible = mVisible = mRequestedVisible;
                 mWindowSpaceLeft = mLocation[0];
                 mWindowSpaceTop = mLocation[1];
-                mSurfaceWidth = myWidth;
-                mSurfaceHeight = myHeight;
+                mWindowSpaceWidth = myWidth;
+                mWindowSpaceHeight = myHeight;
                 mFormat = mRequestedFormat;
-                mLastWindowVisibility = mWindowVisibility;
 
-                mScreenRect.left = mWindowSpaceLeft;
-                mScreenRect.top = mWindowSpaceTop;
-                mScreenRect.right = mWindowSpaceLeft + getWidth();
-                mScreenRect.bottom = mWindowSpaceTop + getHeight();
+                // Scaling/Translate window's layout here because mLayout is not used elsewhere.
+
+                // Places the window relative
+                mLayout.x = mWindowSpaceLeft;
+                mLayout.y = mWindowSpaceTop;
+                mLayout.width = getWidth();
+                mLayout.height = getHeight();
                 if (mTranslator != null) {
-                    mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+                    mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
                 }
 
-                if (creating) {
-                    mSurfaceSession = new SurfaceSession(viewRoot.mSurface);
-                    mSurfaceControl = new SurfaceControl(mSurfaceSession,
-                            "SurfaceView - " + viewRoot.getTitle().toString(),
-                            mSurfaceWidth, mSurfaceHeight, mFormat,
-                            mSurfaceFlags);
+                mLayout.format = mRequestedFormat;
+                mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                              | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                              | WindowManager.LayoutParams.FLAG_SCALED
+                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                              | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                              ;
+                if (!creating && !sizeChanged) {
+                    mLayout.privateFlags |=
+                            WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
+                } else {
+                    mLayout.privateFlags &=
+                            ~WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
                 }
 
-                boolean realSizeChanged = false;
+                if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
+                    mLayout.privateFlags |=
+                            WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
+                }
+                mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+                    | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
+
+                if (mWindow == null) {
+                    Display display = getDisplay();
+                    mWindow = new MyWindow(this);
+                    mLayout.type = mWindowType;
+                    mLayout.gravity = Gravity.START|Gravity.TOP;
+                    mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
+                            mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets,
+                            mStableInsets);
+                }
+
+                boolean realSizeChanged;
+                boolean reportDrawNeeded;
+
+                int relayoutResult;
 
                 mSurfaceLock.lock();
                 try {
+                    mUpdateWindowNeeded = false;
+                    reportDrawNeeded = mReportDrawNeeded;
+                    mReportDrawNeeded = false;
                     mDrawingStopped = !visible;
 
                     if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                             + "Cur surface: " + mSurface);
 
-                    SurfaceControl.openTransaction();
-                    try {
-                        mSurfaceControl.setLayer(mSubLayer);
-                        if (mViewVisibility) {
-                            mSurfaceControl.show();
-                        } else {
-                            mSurfaceControl.hide();
-                        }
-
-                        // While creating the surface, we will set it's initial
-                        // geometry. Outside of that though, we should generally
-                        // leave it to the RenderThread.
-                        if (creating || !mRtHandlingPositionUpdates) {
-                            mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top);
-                            mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
-                                    0.0f, 0.0f,
-                                    mScreenRect.height() / (float) mSurfaceHeight);
-                        }
-                        if (sizeChanged) {
-                            mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
-                        }
-                    } finally {
-                        SurfaceControl.closeTransaction();
+                    relayoutResult = mSession.relayout(
+                        mWindow, mWindow.mSeq, mLayout, mWindowSpaceWidth, mWindowSpaceHeight,
+                            visible ? VISIBLE : GONE,
+                            WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
+                            mWinFrame, mOverscanInsets, mContentInsets,
+                            mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
+                            mConfiguration, mNewSurface);
+                    if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
+                        reportDrawNeeded = true;
                     }
 
-                    if (sizeChanged || creating) {
-                        redrawNeeded = true;
-                    }
+                    if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                            + "New surface: " + mNewSurface
+                            + ", vis=" + visible + ", frame=" + mWinFrame);
 
                     mSurfaceFrame.left = 0;
                     mSurfaceFrame.top = 0;
                     if (mTranslator == null) {
-                        mSurfaceFrame.right = mSurfaceWidth;
-                        mSurfaceFrame.bottom = mSurfaceHeight;
+                        mSurfaceFrame.right = mWinFrame.width();
+                        mSurfaceFrame.bottom = mWinFrame.height();
                     } else {
                         float appInvertedScale = mTranslator.applicationInvertedScale;
-                        mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
-                        mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
+                        mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
+                        mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
                     }
 
                     final int surfaceWidth = mSurfaceFrame.right;
@@ -527,11 +576,12 @@
                 }
 
                 try {
-                    redrawNeeded |= visible && !mDrawFinished;
+                    redrawNeeded |= creating | reportDrawNeeded;
 
                     SurfaceHolder.Callback callbacks[] = null;
 
-                    final boolean surfaceChanged = creating;
+                    final boolean surfaceChanged = (relayoutResult
+                            & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
                     if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
                         mSurfaceCreated = false;
                         if (mSurface.isValid()) {
@@ -558,10 +608,7 @@
                         }
                     }
 
-                    if (creating) {
-                        mSurface.copyFrom(mSurfaceControl);
-                    }
-
+                    mSurface.transferFrom(mNewSurface);
                     if (visible && mSurface.isValid()) {
                         if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
                             mSurfaceCreated = true;
@@ -594,57 +641,53 @@
                                 callbacks = getSurfaceCallbacks();
                             }
                             SurfaceCallbackHelper sch =
-                                    new SurfaceCallbackHelper(this::onDrawFinished);
+                                    new SurfaceCallbackHelper(mSession, mWindow);
                             sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
                         }
                     }
                 } finally {
                     mIsCreating = false;
-                    if (mSurfaceControl != null && !mSurfaceCreated) {
-                        mSurfaceControl.destroy();
-                        mSurfaceControl = null;
-                    }
+                    mSession.performDeferredDestroy(mWindow);
                 }
-            } catch (Exception ex) {
+            } catch (RemoteException ex) {
                 Log.e(TAG, "Exception from relayout", ex);
             }
             if (DEBUG) Log.v(
-                TAG, "Layout: x=" + mScreenRect.left + " y=" + mScreenRect.top
-                + " w=" + mScreenRect.width() + " h=" + mScreenRect.height()
-                + ", frame=" + mSurfaceFrame);
+                TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
+                " w=" + mLayout.width + " h=" + mLayout.height +
+                ", frame=" + mSurfaceFrame);
         } else {
             // Calculate the window position in case RT loses the window
             // and we need to fallback to a UI-thread driven position update
             getLocationInWindow(mLocation);
             final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
                     || mWindowSpaceTop != mLocation[1];
-            final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
-                    || getHeight() != mScreenRect.height();
             if (positionChanged || layoutSizeChanged) { // Only the position has changed
                 mWindowSpaceLeft = mLocation[0];
                 mWindowSpaceTop = mLocation[1];
-                // For our size changed check, we keep mScreenRect.width() and mScreenRect.height()
+                // For our size changed check, we keep mLayout.width and mLayout.height
                 // in view local space.
-                mLocation[0] = getWidth();
-                mLocation[1] = getHeight();
+                mLocation[0] = mLayout.width = getWidth();
+                mLocation[1] = mLayout.height = getHeight();
 
                 transformFromViewToWindowSpace(mLocation);
 
-                mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop,
+                mTmpRect.set(mWindowSpaceLeft, mWindowSpaceTop,
                         mLocation[0], mLocation[1]);
 
                 if (mTranslator != null) {
-                    mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+                    mTranslator.translateRectInAppWindowToScreen(mTmpRect);
                 }
 
                 if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
                     try {
-                        if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition UI, " +
+                        if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition UI, " +
                                 "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
-                                mScreenRect.left, mScreenRect.top,
-                                mScreenRect.right, mScreenRect.bottom));
-                        setParentSpaceRectangle(mScreenRect, -1);
-                    } catch (Exception ex) {
+                                mTmpRect.left, mTmpRect.top,
+                                mTmpRect.right, mTmpRect.bottom));
+                        mSession.repositionChild(mWindow, mTmpRect.left, mTmpRect.top,
+                                mTmpRect.right, mTmpRect.bottom, -1, mTmpRect);
+                    } catch (RemoteException ex) {
                         Log.e(TAG, "Exception from relayout", ex);
                     }
                 }
@@ -652,40 +695,18 @@
         }
     }
 
-    private void onDrawFinished() {
-        if (DEBUG) {
-            Log.i(TAG, System.identityHashCode(this) + " "
-                    + "finishedDrawing");
-        }
-        mHandler.sendEmptyMessage(DRAW_FINISHED_MSG);
-    }
-
-    private void setParentSpaceRectangle(Rect position, long frameNumber) {
-        ViewRootImpl viewRoot = getViewRootImpl();
-
-        SurfaceControl.openTransaction();
-        try {
-            if (frameNumber > 0) {
-                mSurfaceControl.deferTransactionUntil(viewRoot.mSurface, frameNumber);
-            }
-            mSurfaceControl.setPosition(position.left, position.top);
-            mSurfaceControl.setMatrix(position.width() / (float) mSurfaceWidth,
-                    0.0f, 0.0f,
-                    position.height() / (float) mSurfaceHeight);
-        } finally {
-            SurfaceControl.closeTransaction();
-        }
-    }
-
     private Rect mRTLastReportedPosition = new Rect();
 
     /**
      * Called by native by a Rendering Worker thread to update the window position
      * @hide
      */
-    public final void updateSurfacePosition_renderWorker(long frameNumber,
+    public final void updateWindowPosition_renderWorker(long frameNumber,
             int left, int top, int right, int bottom) {
-        if (mSurfaceControl == null) {
+        IWindowSession session = mSession;
+        MyWindow window = mWindow;
+        if (session == null || window == null) {
+            // Guess we got detached, that sucks
             return;
         }
         // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
@@ -705,29 +726,35 @@
         }
         try {
             if (DEBUG) {
-                Log.d(TAG, String.format("%d updateSurfacePosition RenderWorker, frameNr = %d, " +
+                Log.d(TAG, String.format("%d updateWindowPosition RenderWorker, frameNr = %d, " +
                         "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
                         frameNumber, left, top, right, bottom));
             }
-            mRTLastReportedPosition.set(left, top, right, bottom);
-            setParentSpaceRectangle(mRTLastReportedPosition, frameNumber);
+            // Just using mRTLastReportedPosition as a dummy rect here
+            session.repositionChild(window, left, top, right, bottom,
+                    frameNumber,
+                    mRTLastReportedPosition);
             // Now overwrite mRTLastReportedPosition with our values
-        } catch (Exception ex) {
+            mRTLastReportedPosition.set(left, top, right, bottom);
+        } catch (RemoteException ex) {
             Log.e(TAG, "Exception from repositionChild", ex);
         }
     }
 
     /**
-     * Called by native on RenderThread to notify that the view is no longer in the
+     * Called by native on RenderThread to notify that the window is no longer in the
      * draw tree. UI thread is blocked at this point.
      * @hide
      */
-    public final void surfacePositionLost_uiRtSync(long frameNumber) {
+    public final void windowPositionLost_uiRtSync(long frameNumber) {
         if (DEBUG) {
             Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
                     System.identityHashCode(this), frameNumber));
         }
-        if (mSurfaceControl == null) {
+        IWindowSession session = mSession;
+        MyWindow window = mWindow;
+        if (session == null || window == null) {
+            // We got detached prior to receiving this, abort
             return;
         }
         if (mRtHandlingPositionUpdates) {
@@ -736,14 +763,19 @@
             // safely access other member variables at this time.
             // So do what the UI thread would have done if RT wasn't handling position
             // updates.
-            if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) {
+            mTmpRect.set(mLayout.x, mLayout.y,
+                    mLayout.x + mLayout.width,
+                    mLayout.y + mLayout.height);
+
+            if (!mTmpRect.isEmpty() && !mTmpRect.equals(mRTLastReportedPosition)) {
                 try {
-                    if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition, " +
+                    if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition, " +
                             "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
-                            mScreenRect.left, mScreenRect.top,
-                            mScreenRect.right, mScreenRect.bottom));
-                    setParentSpaceRectangle(mScreenRect, frameNumber);
-                } catch (Exception ex) {
+                            mTmpRect.left, mTmpRect.top,
+                            mTmpRect.right, mTmpRect.bottom));
+                    session.repositionChild(window, mTmpRect.left, mTmpRect.top,
+                            mTmpRect.right, mTmpRect.bottom, frameNumber, mWinFrame);
+                } catch (RemoteException ex) {
                     Log.e(TAG, "Exception from relayout", ex);
                 }
             }
@@ -760,6 +792,10 @@
         return callbacks;
     }
 
+    void handleGetNewSurface() {
+        updateWindow();
+    }
+
     /**
      * Check to see if the surface has fixed size dimensions or if the surface's
      * dimensions are dimensions are dependent on its current layout.
@@ -771,8 +807,65 @@
         return (mRequestedWidth != -1 || mRequestedHeight != -1);
     }
 
-    private boolean isAboveParent() {
-        return mSubLayer >= 0;
+    private static class MyWindow extends BaseIWindow {
+        private final WeakReference<SurfaceView> mSurfaceView;
+
+        public MyWindow(SurfaceView surfaceView) {
+            mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
+        }
+
+        @Override
+        public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
+                Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
+                Configuration newConfig, Rect backDropRect, boolean forceLayout,
+                boolean alwaysConsumeNavBar, int displayId) {
+            SurfaceView surfaceView = mSurfaceView.get();
+            if (surfaceView != null) {
+                if (DEBUG) Log.v(TAG, surfaceView + " got resized: w=" + frame.width()
+                        + " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight);
+                surfaceView.mSurfaceLock.lock();
+                try {
+                    if (reportDraw) {
+                        surfaceView.mUpdateWindowNeeded = true;
+                        surfaceView.mReportDrawNeeded = true;
+                        surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
+                    } else if (surfaceView.mWinFrame.width() != frame.width()
+                            || surfaceView.mWinFrame.height() != frame.height()
+                            || forceLayout) {
+                        surfaceView.mUpdateWindowNeeded = true;
+                        surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
+                    }
+                } finally {
+                    surfaceView.mSurfaceLock.unlock();
+                }
+            }
+        }
+
+        @Override
+        public void dispatchAppVisibility(boolean visible) {
+            // The point of SurfaceView is to let the app control the surface.
+        }
+
+        @Override
+        public void dispatchGetNewSurface() {
+            SurfaceView surfaceView = mSurfaceView.get();
+            if (surfaceView != null) {
+                Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
+                surfaceView.mHandler.sendMessage(msg);
+            }
+        }
+
+        @Override
+        public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
+            Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
+        }
+
+        @Override
+        public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
+        }
+
+        int mCurWidth = -1;
+        int mCurHeight = -1;
     }
 
     private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
@@ -820,14 +913,15 @@
 
         @Override
         public void setFormat(int format) {
+
             // for backward compatibility reason, OPAQUE always
             // means 565 for SurfaceView
             if (format == PixelFormat.OPAQUE)
                 format = PixelFormat.RGB_565;
 
             mRequestedFormat = format;
-            if (mSurfaceControl != null) {
-                updateSurface();
+            if (mWindow != null) {
+                updateWindow();
             }
         }
 
@@ -888,10 +982,10 @@
             mSurfaceLock.lock();
 
             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped="
-                    + mDrawingStopped + ", surfaceControl=" + mSurfaceControl);
+                    + mDrawingStopped + ", win=" + mWindow);
 
             Canvas c = null;
-            if (!mDrawingStopped && mSurfaceControl != null) {
+            if (!mDrawingStopped && mWindow != null) {
                 try {
                     if (hardware) {
                         c = mSurface.lockHardwareCanvas();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e349170..99051a7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -974,6 +974,143 @@
      */
     public static final int AUTO_FILL_MODE_MANUAL = 2;
 
+    /** @hide */
+    @IntDef({
+            AUTO_FILL_HINT_NONE,
+            AUTO_FILL_HINT_EMAIL_ADDRESS,
+            AUTO_FILL_HINT_NAME,
+            AUTO_FILL_HINT_POSTAL_ADDRESS,
+            AUTO_FILL_HINT_PASSWORD,
+            AUTO_FILL_HINT_PHONE,
+            AUTO_FILL_HINT_USERNAME,
+            AUTO_FILL_HINT_POSTAL_CODE,
+            AUTO_FILL_HINT_CREDIT_CARD_NUMBER,
+            AUTO_FILL_HINT_CREDIT_CARD_SECURITY_CODE,
+            AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DATE,
+            AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_MONTH,
+            AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_YEAR,
+            AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DAY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AutoFillHint {}
+
+    /**
+     * No auto-fill hint is set.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_NONE = 0;
+
+    /**
+     * This view contains an email address.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_EMAIL_ADDRESS = 0x1;
+
+    /**
+     * The view contains a real name.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_NAME = 0x2;
+
+    /**
+     * The view contains a user name.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_USERNAME = 0x4;
+
+    /**
+     * The view contains a password.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_PASSWORD = 0x8;
+
+    /**
+     * The view contains a phone number.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_PHONE = 0x10;
+
+    /**
+     * The view contains a postal address.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_POSTAL_ADDRESS = 0x20;
+
+    /**
+     * The view contains a postal code.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_POSTAL_CODE = 0x40;
+
+    /**
+     * The view contains a credit card number.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_CREDIT_CARD_NUMBER = 0x80;
+
+    /**
+     * The view contains a credit card security code.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_CREDIT_CARD_SECURITY_CODE = 0x100;
+
+    /**
+     * The view contains a credit card expiration date.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 0x200;
+
+    /**
+     * The view contains the month a credit card expires.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 0x400;
+
+    /**
+     * The view contains the year a credit card expires.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = 0x800;
+
+    /**
+     * The view contains the day a credit card expires.
+     *
+     * Use with {@link #setAutoFillHint(int)} and <a href="#attr_android:autoFillHint">
+     * {@code android:autoFillHint}.
+     */
+    public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 0x1000;
+
+    /**
+     * Hint for the auto-fill services that describes the content of the view.
+     */
+    @AutoFillHint private int mAutoFillHint;
+
     /**
      * This view is enabled. Interpretation varies by subclass.
      * Use with ENABLED_MASK when calling setFlags.
@@ -4799,11 +4936,16 @@
                         setFocusedByDefault(a.getBoolean(attr, true));
                     }
                     break;
-                case com.android.internal.R.styleable.View_autoFillMode:
+                case R.styleable.View_autoFillMode:
                     if (a.peekValue(attr) != null) {
                         setAutoFillMode(a.getInt(attr, AUTO_FILL_MODE_INHERIT));
                     }
                     break;
+                case R.styleable.View_autoFillHint:
+                    if (a.peekValue(attr) != null) {
+                        setAutoFillHint(a.getInt(attr, AUTO_FILL_HINT_NONE));
+                    }
+                    break;
             }
         }
 
@@ -7035,6 +7177,7 @@
                 // to reuse the accessibility id to save space.
                 structure.setAutoFillId(getAccessibilityViewId());
                 structure.setAutoFillType(autoFillType);
+                structure.setAutoFillHint(getAutoFillHint());
                 structure.setAutoFillValue(getAutoFillValue());
             }
         }
@@ -7178,7 +7321,7 @@
     /**
      * Describes the auto-fill type that should be used on calls to
      * {@link #autoFill(AutoFillValue)} and {@link #autoFillVirtual(int, AutoFillValue)}.
-
+     *
      * <p>By default returns {@code null}, but views should override it (and
      * {@link #autoFill(AutoFillValue)} to support the AutoFill Framework.
      */
@@ -7188,9 +7331,21 @@
     }
 
     /**
+     * Describes the content of a view so that a auto-fill service can fill in the appropriate data.
+     *
+     * @return The hint set via the attribute
+     *
+     * @attr ref android.R.styleable#View_autoFillHint
+     */
+    @ViewDebug.ExportedProperty()
+    @AutoFillHint public int getAutoFillHint() {
+        return mAutoFillHint;
+    }
+
+    /**
      * Gets the {@link View}'s current auto-fill value.
      *
-     * <p>By default returns {@code null}, but views should override it,
+     * <p>By default returns {@code null}, but views should override it (and
      * {@link #autoFill(AutoFillValue)}, and {@link #getAutoFillType()} to support the AutoFill
      * Framework.
      */
@@ -8698,6 +8853,17 @@
     }
 
     /**
+     * Sets the a hint that helps the auto-fill service to select the appropriate data to fill the
+     * view.
+     *
+     * @param autoFillHint The auto-fill hint to set
+     * @attr ref android.R.styleable#View_autoFillHint
+     */
+    public void setAutoFillHint(@AutoFillHint int autoFillHint) {
+        mAutoFillHint = autoFillHint;
+    }
+
+    /**
      * Set whether this view should have sound effects enabled for events such as
      * clicking and touching.
      *
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f9863b0..20d960f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2632,14 +2632,6 @@
         }
     }
 
-    private void onDrawFinished() {
-        try {
-            mWindowSession.finishDrawing(mWindow);
-        } catch (RemoteException e) {
-            // Have fun!
-        }
-    }
-
     private void performDraw() {
         if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
             return;
@@ -2690,7 +2682,7 @@
             }
 
             if (mSurfaceHolder != null && mSurface.isValid()) {
-                SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::onDrawFinished);
+                SurfaceCallbackHelper sch = new SurfaceCallbackHelper(mWindowSession, mWindow);
                 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
 
                 sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 84c2c84..bc0960c 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -301,6 +301,13 @@
     public abstract void setAutoFillType(AutoFillType info);
 
     /**
+     * Sets the a hint that helps the auto-fill service to select the appropriate data to fill the
+     * view.
+     */
+    // TODO(b/35364993): add CTS/unit test
+    public abstract void setAutoFillHint(@View.AutoFillHint int hint);
+
+    /**
      * Sets the {@link AutoFillValue} representing the current value of this node.
      */
     // TODO(b/33197203, b/33802548): add CTS/unit test
@@ -316,6 +323,13 @@
     public abstract void setAutoFillOptions(String[] options);
 
     /**
+     * Sets the {@link android.text.InputType} bits of this node.
+     *
+     * @param inputType inputType bits as defined by {@link android.text.InputType}.
+     */
+    public abstract void setInputType(int inputType);
+
+    /**
      * Marks this node as sanitized so its content are sent on {@link
      * android.service.autofill.AutoFillService#onFillRequest(android.app.assist.AssistStructure,
      * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)}.
diff --git a/core/java/android/view/autofill/AutoFillType.java b/core/java/android/view/autofill/AutoFillType.java
index 5d85bfd..62913d8 100644
--- a/core/java/android/view/autofill/AutoFillType.java
+++ b/core/java/android/view/autofill/AutoFillType.java
@@ -21,16 +21,11 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.View;
-import android.widget.TextView;
 
 /**
  * Defines the type of a object that can be used to auto-fill a {@link View} so the
  * {@link android.service.autofill.AutoFillService} can use the proper {@link AutoFillValue} to
  * fill it.
- *
- * <p>Some {@link AutoFillType}s can have an optional {@code sub-type}: the
- * main {@code type} defines the view's UI control category (like a text field), while the optional
- * {@code sub-type} define its semantics (like a postal address).
  */
 public final class AutoFillType implements Parcelable {
 
@@ -38,37 +33,29 @@
     // class idiom" (Effective Java, Item 71) to avoid memory utilization when auto-fill is not
     // enabled.
     private static class DefaultTypesHolder {
-        static final AutoFillType TOGGLE = new AutoFillType(TYPE_TOGGLE, 0);
-        static final AutoFillType LIST = new AutoFillType(TYPE_LIST, 0);
+        static final AutoFillType TEXT = new AutoFillType(TYPE_TEXT);
+        static final AutoFillType TOGGLE = new AutoFillType(TYPE_TOGGLE);
+        static final AutoFillType LIST = new AutoFillType(TYPE_LIST);
+        static final AutoFillType DATE = new AutoFillType(TYPE_DATE);
     }
 
     private static final int TYPE_TEXT = 1;
     private static final int TYPE_TOGGLE = 2;
     private static final int TYPE_LIST = 3;
-
-    // TODO(b/33197203): add others, like date picker? That would be trick, because they're set as:
-    // updateDate(int year, int month, int dayOfMonth)
-    // So, we would have to either use a long representing the Date.time(), or a custom long
-    // representing:
-    // year * 10000 + month * 100 + day
-    // Then a custom getDatePickerValue(Bundle) that returns an immutable object with these 3 fields
+    private static final int TYPE_DATE = 4;
 
     private final int mType;
-    private final int mSubType;
 
-    private AutoFillType(int type, int subType) {
+    private AutoFillType(int type) {
         mType = type;
-        mSubType = subType;
     }
 
     /**
      * Checks if this is a type for a text field, which is filled by a {@link CharSequence}.
      *
      * <p>{@link AutoFillValue} instances for auto-filling a {@link View} can be obtained through
-     * {@link AutoFillValue#forText(CharSequence)}, and the value of a bundle passed to auto-fill a
+     * {@link AutoFillValue#forText(CharSequence)}, and the value passed to auto-fill a
      * {@link View} can be fetched through {@link AutoFillValue#getTextValue()}.
-     *
-     * <p>Sub-type for this type is the value defined by {@link TextView#getInputType()}.
      */
     public boolean isText() {
         return mType == TYPE_TEXT;
@@ -78,10 +65,8 @@
      * Checks if this is a a type for a togglable field, which is filled by a {@code boolean}.
      *
      * <p>{@link AutoFillValue} instances for auto-filling a {@link View} can be obtained through
-     * {@link AutoFillValue#forToggle(boolean)}, and the value of a bundle passed to auto-fill a
+     * {@link AutoFillValue#forToggle(boolean)}, and the value passed to auto-fill a
      * {@link View} can be fetched through {@link AutoFillValue#getToggleValue()}.
-     *
-     * <p>This type has no sub-types.
      */
     public boolean isToggle() {
         return mType == TYPE_TOGGLE;
@@ -92,23 +77,26 @@
      * representing the element index inside the list (starting at {@code 0}.
      *
      * <p>{@link AutoFillValue} instances for auto-filling a {@link View} can be obtained through
-     * {@link AutoFillValue#forList(int)}, and the value of a bundle passed to auto-fill a
+     * {@link AutoFillValue#forList(int)}, and the value passed to auto-fill a
      * {@link View} can be fetched through {@link AutoFillValue#getListValue()}.
-     *
-     * <p>This type has no sub-types.
      */
     public boolean isList() {
         return mType == TYPE_LIST;
     }
 
-
     /**
-     * Gets the optional sub-type, representing the {@link View}'s semantic.
+     * Checks if this is a type for a date and time, which is represented by a long representing
+     * the number of milliseconds since the standard base time known as "the epoch", namely
+     * January 1, 1970, 00:00:00 GMT (see {@link java.util.Date#getTime()}.
      *
-     * @return {@code 0} if type does not support sub-types.
+     * <p>{@link AutoFillValue} instances for auto-filling a {@link View} can be obtained through
+     * {@link AutoFillValue#forDate(long)}, and the values passed to
+     * auto-fill a {@link View} can be fetched through {@link AutoFillValue#getDateValue()}.
+     *
+     * <p>This type has no sub-types.
      */
-    public int getSubType() {
-        return mSubType;
+    public boolean isDate() {
+        return mType == TYPE_DATE;
     }
 
     /////////////////////////////////////
@@ -119,14 +107,13 @@
     public String toString() {
         if (!DEBUG) return super.toString();
 
-        return "AutoFillType [type=" + mType + ", subType=" + mSubType + "]";
+        return "AutoFillType [type=" + mType + "]";
     }
 
     @Override
     public int hashCode() {
         final int prime = 31;
         int result = 1;
-        result = prime * result + mSubType;
         result = prime * result + mType;
         return result;
     }
@@ -137,7 +124,6 @@
         if (obj == null) return false;
         if (getClass() != obj.getClass()) return false;
         final AutoFillType other = (AutoFillType) obj;
-        if (mSubType != other.mSubType) return false;
         if (mType != other.mType) return false;
         return true;
     }
@@ -154,12 +140,10 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(mType);
-        parcel.writeInt(mSubType);
     }
 
     private AutoFillType(Parcel parcel) {
         mType = parcel.readInt();
-        mSubType = parcel.readInt();
     }
 
     public static final Parcelable.Creator<AutoFillType> CREATOR =
@@ -184,8 +168,8 @@
      *
      * <p>See {@link #isText()} for more info.
      */
-    public static AutoFillType forText(int inputType) {
-        return new AutoFillType(TYPE_TEXT, inputType);
+    public static AutoFillType forText() {
+        return DefaultTypesHolder.TEXT;
     }
 
     /**
@@ -206,4 +190,13 @@
     public static AutoFillType forList() {
         return DefaultTypesHolder.LIST;
     }
+
+    /**
+     * Creates a type that represents a date.
+     *
+     * <p>See {@link #isDate()} for more info.
+     */
+    public static AutoFillType forDate() {
+        return DefaultTypesHolder.DATE;
+    }
 }
diff --git a/core/java/android/view/autofill/AutoFillValue.java b/core/java/android/view/autofill/AutoFillValue.java
index d9afa3b..c24e04e 100644
--- a/core/java/android/view/autofill/AutoFillValue.java
+++ b/core/java/android/view/autofill/AutoFillValue.java
@@ -35,11 +35,13 @@
     private final String mText;
     private final int mListIndex;
     private final boolean mToggle;
+    private final long mDate;
 
-    private AutoFillValue(CharSequence text, int listIndex, boolean toggle) {
+    private AutoFillValue(CharSequence text, int listIndex, boolean toggle, long date) {
         mText = (text == null) ? null : text.toString();
         mListIndex = listIndex;
         mToggle = toggle;
+        mDate = date;
     }
 
     /**
@@ -69,6 +71,17 @@
         return mListIndex;
     }
 
+    /**
+     * Gets the value representing the the number of milliseconds since the standard base time known
+     * as "the epoch", namely January 1, 1970, 00:00:00 GMT (see {@link java.util.Date#getTime()}
+     * of a date field.
+     *
+     * <p>See {@link AutoFillType#isDate()} for more info.
+     */
+    public long getDateValue() {
+        return mDate;
+    }
+
     /////////////////////////////////////
     //  Object "contract" methods. //
     /////////////////////////////////////
@@ -77,9 +90,10 @@
     public int hashCode() {
         final int prime = 31;
         int result = 1;
-        result = prime * result + mListIndex;
         result = prime * result + ((mText == null) ? 0 : mText.hashCode());
+        result = prime * result + mListIndex;
         result = prime * result + (mToggle ? 1231 : 1237);
+        result = prime * result + (int) (mDate ^ (mDate >>> 32));
         return result;
     }
 
@@ -89,13 +103,14 @@
         if (obj == null) return false;
         if (getClass() != obj.getClass()) return false;
         final AutoFillValue other = (AutoFillValue) obj;
-        if (mListIndex != other.mListIndex) return false;
         if (mText == null) {
             if (other.mText != null) return false;
         } else {
             if (!mText.equals(other.mText)) return false;
         }
+        if (mListIndex != other.mListIndex) return false;
         if (mToggle != other.mToggle) return false;
+        if (mDate != other.mDate) return false;
         return true;
     }
 
@@ -113,7 +128,7 @@
             return mText.length() + "_chars";
         }
 
-        return "[listIndex=" + mListIndex + ", toggle=" + mToggle + "]";
+        return "[l=" + mListIndex + ", t=" + mToggle + ", d=" + mDate + "]";
     }
 
     /////////////////////////////////////
@@ -130,12 +145,14 @@
         parcel.writeString(mText);
         parcel.writeInt(mListIndex);
         parcel.writeInt(mToggle ? 1 : 0);
+        parcel.writeLong(mDate);
     }
 
     private AutoFillValue(Parcel parcel) {
         mText = parcel.readString();
         mListIndex = parcel.readInt();
         mToggle = parcel.readInt() == 1;
+        mDate = parcel.readLong();
     }
 
     public static final Parcelable.Creator<AutoFillValue> CREATOR =
@@ -157,31 +174,42 @@
 
     // TODO(b/33197203): add unit tests for each supported type (new / get should return same value)
     /**
-     * Creates a new {@link AutoFillValue} to auto-fill a text field.
+     * Creates a new {@link AutoFillValue} to auto-fill a {@link View} representing a text field.
      *
      * <p>See {@link AutoFillType#isText()} for more info.
      */
     // TODO(b/33197203): use cache
     @Nullable
     public static AutoFillValue forText(@Nullable CharSequence value) {
-        return value == null ? null : new AutoFillValue(value, 0, false);
+        return value == null ? null : new AutoFillValue(value, 0, false, 0);
     }
 
     /**
-     * Creates a new {@link AutoFillValue} to auto-fill a toggable field.
+     * Creates a new {@link AutoFillValue} to auto-fill a {@link View} representing a toggable
+     * field.
      *
      * <p>See {@link AutoFillType#isToggle()} for more info.
      */
     public static AutoFillValue forToggle(boolean value) {
-        return new AutoFillValue(null, 0, value);
+        return new AutoFillValue(null, 0, value, 0);
     }
 
     /**
-     * Creates a new {@link AutoFillValue} to auto-fill a selection list field.
+     * Creates a new {@link AutoFillValue} to auto-fill a {@link View} representing a selection
+     * list.
      *
      * <p>See {@link AutoFillType#isList()} for more info.
      */
     public static AutoFillValue forList(int value) {
-        return new AutoFillValue(null, value, false);
+        return new AutoFillValue(null, value, false, 0);
+    }
+
+    /**
+     * Creates a new {@link AutoFillValue} to auto-fill a {@link View} representing a date.
+     *
+     * <p>See {@link AutoFillType#isDate()} for more info.
+     */
+    public static AutoFillValue forDate(long date) {
+        return new AutoFillValue(null, 0, false, date);
     }
 }
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 887c59a..141b52f 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -591,7 +591,7 @@
 
     @Override
     public AutoFillType getAutoFillType() {
-        return AutoFillType.forToggle();
+        return isEnabled() ? AutoFillType.forToggle() : null;
     }
 
     @Override
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 517e20cd..0ffefd0 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -31,7 +31,11 @@
 import android.util.AttributeSet;
 import android.util.SparseArray;
 import android.view.View;
+import android.view.ViewStructure;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.autofill.AutoFillManager;
+import android.view.autofill.AutoFillType;
+import android.view.autofill.AutoFillValue;
 
 import com.android.internal.R;
 
@@ -170,6 +174,13 @@
         if (firstDayOfWeek != 0) {
             setFirstDayOfWeek(firstDayOfWeek);
         }
+
+        mDelegate.setAutoFillChangeListener((v, y, m, d) -> {
+            final AutoFillManager afm = context.getSystemService(AutoFillManager.class);
+            if (afm != null) {
+                afm.valueChanged(this);
+            }
+        });
     }
 
     private DatePickerDelegate createSpinnerUIDelegate(Context context, AttributeSet attrs,
@@ -503,12 +514,15 @@
                   OnDateChangedListener onDateChangedListener);
 
         void setOnDateChangedListener(OnDateChangedListener onDateChangedListener);
+        void setAutoFillChangeListener(OnDateChangedListener onDateChangedListener);
 
         void updateDate(int year, int month, int dayOfMonth);
+        void updateDate(long date);
 
         int getYear();
         int getMonth();
         int getDayOfMonth();
+        long getDate();
 
         void setFirstDayOfWeek(int firstDayOfWeek);
         int getFirstDayOfWeek();
@@ -558,6 +572,7 @@
 
         // Callbacks
         protected OnDateChangedListener mOnDateChangedListener;
+        protected OnDateChangedListener mAutoFillChangeListener;
         protected ValidationCallback mValidationCallback;
 
         public AbstractDatePickerDelegate(DatePicker delegator, Context context) {
@@ -580,10 +595,28 @@
         }
 
         @Override
+        public void setAutoFillChangeListener(OnDateChangedListener callback) {
+            mAutoFillChangeListener = callback;
+        }
+
+        @Override
         public void setValidationCallback(ValidationCallback callback) {
             mValidationCallback = callback;
         }
 
+        @Override
+        public void updateDate(long date) {
+            Calendar cal = Calendar.getInstance(mCurrentLocale);
+            cal.setTimeInMillis(date);
+            updateDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
+                    cal.get(Calendar.DAY_OF_MONTH));
+        }
+
+        @Override
+        public long getDate() {
+            return mCurrentDate.getTimeInMillis();
+        }
+
         protected void onValidationChanged(boolean valid) {
             if (mValidationCallback != null) {
                 mValidationCallback.onValidationChanged(valid);
@@ -723,4 +756,31 @@
     public interface ValidationCallback {
         void onValidationChanged(boolean valid);
     }
+
+    // TODO(b/33197203): add unit/CTS tests for auto-fill methods (and make sure they handle enable)
+
+    @Override
+    public void dispatchProvideAutoFillStructure(ViewStructure structure, int flags) {
+        // This view is self-sufficient for auto-fill, so it needs to call
+        // onProvideAutoFillStructure() to fill itself, but it does not need to call
+        // dispatchProvideAutoFillStructure() to fill its children.
+        onProvideAutoFillStructure(structure, flags);
+    }
+
+    @Override
+    public void autoFill(AutoFillValue value) {
+        if (!isEnabled()) return;
+
+        mDelegate.updateDate(value.getDateValue());
+    }
+
+    @Override
+    public AutoFillType getAutoFillType() {
+        return isEnabled() ? AutoFillType.forDate() : null;
+    }
+
+    @Override
+    public AutoFillValue getAutoFillValue() {
+        return isEnabled() ? AutoFillValue.forDate(mDelegate.getDate()) : null;
+    }
 }
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 907250a..ca1bf58 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -390,10 +390,16 @@
     private void onDateChanged(boolean fromUser, boolean callbackToClient) {
         final int year = mCurrentDate.get(Calendar.YEAR);
 
-        if (callbackToClient && mOnDateChangedListener != null) {
+        if (callbackToClient
+                && (mOnDateChangedListener != null || mAutoFillChangeListener != null)) {
             final int monthOfYear = mCurrentDate.get(Calendar.MONTH);
             final int dayOfMonth = mCurrentDate.get(Calendar.DAY_OF_MONTH);
-            mOnDateChangedListener.onDateChanged(mDelegator, year, monthOfYear, dayOfMonth);
+            if (mOnDateChangedListener != null) {
+                mOnDateChangedListener.onDateChanged(mDelegator, year, monthOfYear, dayOfMonth);
+            }
+            if (mAutoFillChangeListener != null) {
+                mAutoFillChangeListener.onDateChanged(mDelegator, year, monthOfYear, dayOfMonth);
+            }
         }
 
         mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java
index 702b2a5..fc2d1fa 100644
--- a/core/java/android/widget/DatePickerSpinnerDelegate.java
+++ b/core/java/android/widget/DatePickerSpinnerDelegate.java
@@ -576,6 +576,10 @@
             mOnDateChangedListener.onDateChanged(mDelegator, getYear(), getMonth(),
                     getDayOfMonth());
         }
+        if (mAutoFillChangeListener != null) {
+            mAutoFillChangeListener.onDateChanged(mDelegator, getYear(), getMonth(),
+                    getDayOfMonth());
+        }
     }
 
     /**
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index bba3a11..be5fc53 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -436,7 +436,7 @@
 
     @Override
     public AutoFillType getAutoFillType() {
-        return AutoFillType.forList();
+        return isEnabled() ? AutoFillType.forList() : null;
     }
 
     @Override
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 4937740..97fc835 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10002,6 +10002,7 @@
                     AssistStructure.ViewNode.TEXT_COLOR_UNDEFINED /* bgColor */, style);
         }
         structure.setHint(getHint());
+        structure.setInputType(getInputType());
     }
 
     // TODO(b/33197203): add unit/CTS tests for auto-fill methods
@@ -10018,7 +10019,7 @@
     @Override
     @Nullable
     public AutoFillType getAutoFillType() {
-        return isTextEditable() ? AutoFillType.forText(getInputType()) : null;
+        return isTextEditable() ? AutoFillType.forText() : null;
     }
 
     @Override
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 9f38b04..1df202e 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -23,12 +23,17 @@
 import android.annotation.Widget;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.icu.util.Calendar;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.util.MathUtils;
 import android.view.View;
+import android.view.ViewStructure;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.autofill.AutoFillManager;
+import android.view.autofill.AutoFillType;
+import android.view.autofill.AutoFillValue;
 
 import com.android.internal.R;
 
@@ -132,6 +137,12 @@
                         this, context, attrs, defStyleAttr, defStyleRes);
                 break;
         }
+        mDelegate.setAutoFillChangeListener((v, h, m) -> {
+            final AutoFillManager afm = context.getSystemService(AutoFillManager.class);
+            if (afm != null) {
+                afm.valueChanged(this);
+            }
+        });
     }
 
     /**
@@ -348,12 +359,16 @@
         void setMinute(@IntRange(from = 0, to = 59) int minute);
         int getMinute();
 
+        void setDate(long date);
+        long getDate();
+
         void setIs24Hour(boolean is24Hour);
         boolean is24Hour();
 
         boolean validateInput();
 
         void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener);
+        void setAutoFillChangeListener(OnTimeChangedListener autoFillChangeListener);
 
         void setEnabled(boolean enabled);
         boolean isEnabled();
@@ -398,6 +413,7 @@
         protected final Locale mLocale;
 
         protected OnTimeChangedListener mOnTimeChangedListener;
+        protected OnTimeChangedListener mAutoFillChangeListener;
 
         public AbstractTimePickerDelegate(@NonNull TimePicker delegator, @NonNull Context context) {
             mDelegator = delegator;
@@ -410,6 +426,27 @@
             mOnTimeChangedListener = callback;
         }
 
+        @Override
+        public void setAutoFillChangeListener(OnTimeChangedListener callback) {
+            mAutoFillChangeListener = callback;
+        }
+
+        @Override
+        public void setDate(long date) {
+            Calendar cal = Calendar.getInstance(mLocale);
+            cal.setTimeInMillis(date);
+            setHour(cal.get(Calendar.HOUR_OF_DAY));
+            setMinute(cal.get(Calendar.MINUTE));
+        }
+
+        @Override
+        public long getDate() {
+            Calendar cal = Calendar.getInstance(mLocale);
+            cal.set(Calendar.HOUR_OF_DAY, getHour());
+            cal.set(Calendar.MINUTE, getMinute());
+            return cal.getTimeInMillis();
+        }
+
         protected static class SavedState extends View.BaseSavedState {
             private final int mHour;
             private final int mMinute;
@@ -474,4 +511,31 @@
             };
         }
     }
+
+    // TODO(b/33197203): add unit/CTS tests for auto-fill methods (and make sure they handle enable)
+
+    @Override
+    public void dispatchProvideAutoFillStructure(ViewStructure structure, int flags) {
+        // This view is self-sufficient for auto-fill, so it needs to call
+        // onProvideAutoFillStructure() to fill itself, but it does not need to call
+        // dispatchProvideAutoFillStructure() to fill its children.
+        onProvideAutoFillStructure(structure, flags);
+    }
+
+    @Override
+    public void autoFill(AutoFillValue value) {
+        if (!isEnabled()) return;
+
+        mDelegate.setDate(value.getDateValue());
+    }
+
+    @Override
+    public AutoFillType getAutoFillType() {
+        return isEnabled() ? AutoFillType.forDate() : null;
+    }
+
+    @Override
+    public AutoFillValue getAutoFillValue() {
+        return isEnabled() ? AutoFillValue.forDate(mDelegate.getDate()) : null;
+    }
 }
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 94bd342..3605585 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -671,6 +671,9 @@
         if (mOnTimeChangedListener != null) {
             mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
         }
+        if (mAutoFillChangeListener != null) {
+            mAutoFillChangeListener.onTimeChanged(mDelegator, getHour(), getMinute());
+        }
     }
 
     private void tryVibrate() {
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 20a5512..813c30e3 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -495,6 +495,9 @@
             mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(),
                     getMinute());
         }
+        if (mAutoFillChangeListener != null) {
+            mAutoFillChangeListener.onTimeChanged(mDelegator, getHour(), getMinute());
+        }
     }
 
     private void updateHourControl() {
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index c840f26..ec3aac2 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -48,12 +48,12 @@
         List<NotificationChannel> channelsList = new ArrayList<NotificationChannel>();
         channelsList.add(new NotificationChannel(
                 VIRTUAL_KEYBOARD,
-                context.getString(R.string.notification_channel_virtual_keyboard),
+                R.string.notification_channel_virtual_keyboard,
                 NotificationManager.IMPORTANCE_LOW));
 
         final NotificationChannel physicalKeyboardChannel = new NotificationChannel(
                 PHYSICAL_KEYBOARD,
-                context.getString(R.string.notification_channel_physical_keyboard),
+                R.string.notification_channel_physical_keyboard,
                 NotificationManager.IMPORTANCE_DEFAULT);
         physicalKeyboardChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
                 Notification.AUDIO_ATTRIBUTES_DEFAULT);
@@ -61,32 +61,32 @@
 
         channelsList.add(new NotificationChannel(
                 SECURITY,
-                context.getString(R.string.notification_channel_security),
+                R.string.notification_channel_security,
                 NotificationManager.IMPORTANCE_LOW));
 
         channelsList.add(new NotificationChannel(
                 CAR_MODE,
-                context.getString(R.string.notification_channel_car_mode),
+                R.string.notification_channel_car_mode,
                 NotificationManager.IMPORTANCE_LOW));
 
         channelsList.add(new NotificationChannel(
                 DEVELOPER,
-                context.getString(R.string.notification_channel_developer),
+                R.string.notification_channel_developer,
                 NotificationManager.IMPORTANCE_LOW));
 
         channelsList.add(new NotificationChannel(
                 UPDATES,
-                context.getString(R.string.notification_channel_updates),
+                R.string.notification_channel_updates,
                 NotificationManager.IMPORTANCE_LOW));
 
         channelsList.add(new NotificationChannel(
                 NETWORK_STATUS,
-                context.getString(R.string.notification_channel_network_status),
+                R.string.notification_channel_network_status,
                 NotificationManager.IMPORTANCE_LOW));
 
         final NotificationChannel networkAlertsChannel = new NotificationChannel(
                 NETWORK_ALERTS,
-                context.getString(R.string.notification_channel_network_alerts),
+                R.string.notification_channel_network_alerts,
                 NotificationManager.IMPORTANCE_HIGH);
         networkAlertsChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
                 Notification.AUDIO_ATTRIBUTES_DEFAULT);
@@ -94,17 +94,17 @@
 
         channelsList.add(new NotificationChannel(
                 VPN,
-                context.getString(R.string.notification_channel_vpn),
+                R.string.notification_channel_vpn,
                 NotificationManager.IMPORTANCE_LOW));
 
         channelsList.add(new NotificationChannel(
                 DEVICE_ADMIN,
-                context.getString(R.string.notification_channel_device_admin),
+                R.string.notification_channel_device_admin,
                 NotificationManager.IMPORTANCE_LOW));
 
         final NotificationChannel alertsChannel = new NotificationChannel(
                 ALERTS,
-                context.getString(R.string.notification_channel_alerts),
+                R.string.notification_channel_alerts,
                 NotificationManager.IMPORTANCE_DEFAULT);
         alertsChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
                 Notification.AUDIO_ATTRIBUTES_DEFAULT);
@@ -112,12 +112,12 @@
 
         channelsList.add(new NotificationChannel(
                 RETAIL_MODE,
-                context.getString(R.string.notification_channel_retail_mode),
+                R.string.notification_channel_retail_mode,
                 NotificationManager.IMPORTANCE_LOW));
 
         channelsList.add(new NotificationChannel(
                 USB,
-                context.getString(R.string.notification_channel_usb),
+                R.string.notification_channel_usb,
                 NotificationManager.IMPORTANCE_MIN));
 
         nm.createNotificationChannels(channelsList);
@@ -128,7 +128,7 @@
         final NotificationManager nm = context.getSystemService(NotificationManager.class);
         nm.createNotificationChannelsForPackage(pkg, Arrays.asList(new NotificationChannel(
                 ACCOUNT,
-                context.getString(R.string.notification_channel_account),
+                R.string.notification_channel_account,
                 NotificationManager.IMPORTANCE_LOW)));
     }
 
diff --git a/core/java/com/android/internal/view/SurfaceCallbackHelper.java b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
index 507b673..5b6a82c 100644
--- a/core/java/com/android/internal/view/SurfaceCallbackHelper.java
+++ b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
@@ -17,11 +17,14 @@
 package com.android.internal.view;
 
 import android.os.RemoteException;
+import android.view.IWindow;
+import android.view.IWindowSession;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 
 public class SurfaceCallbackHelper {
-    Runnable mRunnable;
+    IWindowSession mSession;
+    IWindow.Stub mWindow;
 
     int mFinishDrawingCollected = 0;
     int mFinishDrawingExpected = 0;
@@ -34,18 +37,26 @@
                     if (mFinishDrawingCollected < mFinishDrawingExpected) {
                         return;
                     }
-                    mRunnable.run();
+                    try {
+                        mSession.finishDrawing(mWindow);
+                    } catch (RemoteException e) {
+                    }
                 }
             }
     };
 
-    public SurfaceCallbackHelper(Runnable callbacksCollected) {
-        mRunnable = callbacksCollected;
+    public SurfaceCallbackHelper(IWindowSession session,
+            IWindow.Stub window) {
+        mSession = session;
+        mWindow = window;
     }
 
     public void dispatchSurfaceRedrawNeededAsync(SurfaceHolder holder, SurfaceHolder.Callback callbacks[]) {
         if (callbacks == null || callbacks.length == 0) {
-            mRunnable.run();
+            try {
+                mSession.finishDrawing(mWindow);
+            } catch (RemoteException e) {
+            }
             return;
         }
 
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index c348cc5..a196540 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -232,6 +232,73 @@
     return localBitmap->bitmap();
 }
 
+void imageInfo(JNIEnv* env, jobject bitmap, AndroidBitmapInfo* info) {
+    SkASSERT(info);
+    SkASSERT(env);
+    SkASSERT(bitmap);
+    SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
+    jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
+    LocalScopedBitmap localBitmap(bitmapHandle);
+
+    const SkImageInfo& imageInfo = localBitmap->info();
+    info->width = imageInfo.width();
+    info->height = imageInfo.height();
+    info->stride = localBitmap->rowBytes();
+    info->flags = 0;
+    switch (imageInfo.colorType()) {
+        case kN32_SkColorType:
+            info->format = ANDROID_BITMAP_FORMAT_RGBA_8888;
+            break;
+        case kRGB_565_SkColorType:
+            info->format = ANDROID_BITMAP_FORMAT_RGB_565;
+            break;
+        case kARGB_4444_SkColorType:
+            info->format = ANDROID_BITMAP_FORMAT_RGBA_4444;
+            break;
+        case kAlpha_8_SkColorType:
+            info->format = ANDROID_BITMAP_FORMAT_A_8;
+            break;
+        default:
+            info->format = ANDROID_BITMAP_FORMAT_NONE;
+            break;
+    }
+}
+
+void* lockPixels(JNIEnv* env, jobject bitmap) {
+    SkASSERT(env);
+    SkASSERT(bitmap);
+    SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
+    jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
+
+    LocalScopedBitmap localBitmap(bitmapHandle);
+    if (!localBitmap->valid()) return nullptr;
+
+    SkPixelRef& pixelRef = localBitmap->bitmap();
+    pixelRef.lockPixels();
+    if (!pixelRef.pixels()) {
+        pixelRef.unlockPixels();
+        return nullptr;
+    }
+    pixelRef.ref();
+    return pixelRef.pixels();
+}
+
+bool unlockPixels(JNIEnv* env, jobject bitmap) {
+    SkASSERT(env);
+    SkASSERT(bitmap);
+    SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
+    jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
+
+    LocalScopedBitmap localBitmap(bitmapHandle);
+    if (!localBitmap->valid()) return false;
+
+    SkPixelRef& pixelRef = localBitmap->bitmap();
+    pixelRef.notifyPixelsChanged();
+    pixelRef.unlockPixels();
+    pixelRef.unref();
+    return true;
+}
+
 } // namespace bitmap
 
 } // namespace android
diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h
index 387a128..a4bfc94 100644
--- a/core/jni/android/graphics/Bitmap.h
+++ b/core/jni/android/graphics/Bitmap.h
@@ -17,6 +17,7 @@
 #define BITMAP_H_
 
 #include <jni.h>
+#include <android/bitmap.h>
 #include <SkBitmap.h>
 #include <SkColorTable.h>
 #include <SkImageInfo.h>
@@ -44,6 +45,13 @@
 Bitmap& toBitmap(JNIEnv* env, jobject bitmap);
 Bitmap& toBitmap(JNIEnv* env, jlong bitmapHandle);
 
+// NDK access
+void imageInfo(JNIEnv* env, jobject bitmap, AndroidBitmapInfo* info);
+// Returns a pointer to the pixels or nullptr if the bitmap is not valid
+void* lockPixels(JNIEnv* env, jobject bitmap);
+// Returns true if unlocked, false if the bitmap is no longer valid (destroyed)
+bool unlockPixels(JNIEnv* env, jobject bitmap);
+
 /** Reinitialize a bitmap. bitmap must already have its SkAlphaType set in
     sync with isPremultiplied
 */
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 989a557..2439b82 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -346,7 +346,7 @@
               << serviceName;
 
     ::android::vintf::Transport transport =
-            ::android::hardware::getTransport(ifaceName);
+            ::android::hardware::getTransport(ifaceName, serviceName);
     if (   transport != ::android::vintf::Transport::EMPTY
         && transport != ::android::vintf::Transport::HWBINDER) {
         LOG(ERROR) << "service " << ifaceName << " declares transport method "
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index edcbb3f..f221392 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -627,9 +627,9 @@
 int register_android_view_RenderNode(JNIEnv* env) {
     jclass clazz = FindClassOrDie(env, "android/view/SurfaceView");
     gSurfaceViewPositionUpdateMethod = GetMethodIDOrDie(env, clazz,
-            "updateSurfacePosition_renderWorker", "(JIIII)V");
+            "updateWindowPosition_renderWorker", "(JIIII)V");
     gSurfaceViewPositionLostMethod = GetMethodIDOrDie(env, clazz,
-            "surfacePositionLost_uiRtSync", "(J)V");
+            "windowPositionLost_uiRtSync", "(J)V");
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index be86f5c..a81901d 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -693,6 +693,7 @@
     return JNI_TRUE;
 }
 
+
 static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong nativeObject,
         jobject handleObject, jlong frameNumber) {
     auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
@@ -701,27 +702,6 @@
     ctrl->deferTransactionUntil(handle, frameNumber);
 }
 
-static void nativeDeferTransactionUntilSurface(JNIEnv* env, jclass clazz, jlong nativeObject,
-        jobject surfaceObject, jlong frameNumber) {
-    auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    sp<Surface> barrier = reinterpret_cast<Surface *>(surfaceObject);
-
-    ctrl->deferTransactionUntil(barrier, frameNumber);
-}
-
-static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong nativeObject,
-        jobject newParentObject) {
-    auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    sp<IBinder> handle = ibinderForJavaObject(env, newParentObject);
-
-    ctrl->reparentChildren(handle);
-}
-
-static void nativeSeverChildren(JNIEnv* env, jclass clazz, jlong nativeObject) {
-    auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    ctrl->detachChildren();
-}
-
 static void nativeSetOverrideScalingMode(JNIEnv* env, jclass clazz, jlong nativeObject,
         jint scalingMode) {
     auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
@@ -844,12 +824,6 @@
             (void*)nativeSetDisplayPowerMode },
     {"nativeDeferTransactionUntil", "(JLandroid/os/IBinder;J)V",
             (void*)nativeDeferTransactionUntil },
-    {"nativeDeferTransactionUntilSurface", "(JJJ)V",
-            (void*)nativeDeferTransactionUntilSurface },
-    {"nativeReparentChildren", "(JLandroid/os/IBinder;)V",
-            (void*)nativeReparentChildren } ,
-    {"nativeSeverChildren", "(J)V",
-            (void*)nativeSeverChildren } ,
     {"nativeSetOverrideScalingMode", "(JI)V",
             (void*)nativeSetOverrideScalingMode },
     {"nativeGetHandle", "(J)Landroid/os/IBinder;",
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 508d897..dad6958 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -24,7 +24,6 @@
 #include <utils/RefBase.h>
 
 #include <gui/SurfaceComposerClient.h>
-#include <gui/Surface.h>
 
 namespace android {
 
@@ -46,13 +45,6 @@
     return reinterpret_cast<jlong>(client);
 }
 
-static jlong nativeCreateScoped(JNIEnv* env, jclass clazz, jlong surfaceObject) {
-    Surface *parent = reinterpret_cast<Surface*>(surfaceObject);
-    SurfaceComposerClient* client = new SurfaceComposerClient(parent->getIGraphicBufferProducer());
-    client->incStrong((void*)nativeCreate);
-    return reinterpret_cast<jlong>(client);
-}
-
 static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
     SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
     client->decStrong((void*)nativeCreate);
@@ -63,12 +55,11 @@
     client->dispose();
 }
 
+
 static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
     { "nativeCreate", "()J",
             (void*)nativeCreate },
-    { "nativeCreateScoped", "(J)J",
-            (void*)nativeCreateScoped },
     { "nativeDestroy", "(J)V",
             (void*)nativeDestroy },
     { "nativeKill", "(J)V",
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 59a536b..9660de4 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -26,8 +26,8 @@
 #include <sys/un.h>
 #include <unistd.h>
 
+#include <android-base/logging.h>
 #include <android-base/strings.h>
-#include <cutils/log.h>
 
 // Static whitelist of open paths that the zygote is allowed to keep open.
 static const char* kPathWhitelist[] = {
@@ -137,7 +137,7 @@
   // This should never happen; the zygote should always have the right set
   // of permissions required to stat all its open files.
   if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
-    ALOGE("Unable to stat fd %d : %s", fd, strerror(errno));
+    PLOG(ERROR) << "Unable to stat fd " << fd;
     return NULL;
   }
 
@@ -150,7 +150,8 @@
     }
 
     if (!whitelist->IsAllowed(socket_name)) {
-      ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
+      LOG(ERROR) << "Socket name not whitelisted : " << socket_name
+                 << " (fd=" << fd << ")";
       return NULL;
     }
 
@@ -168,7 +169,7 @@
   // with the child process across forks but those should have been closed
   // before we got to this point.
   if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
-    ALOGE("Unsupported st_mode %d", f_stat.st_mode);
+    LOG(ERROR) << "Unsupported st_mode " << f_stat.st_mode;
     return NULL;
   }
 
@@ -178,7 +179,7 @@
   }
 
   if (!whitelist->IsAllowed(file_path)) {
-    ALOGE("Not whitelisted : %s", file_path.c_str());
+    LOG(ERROR) << "Not whitelisted : " << file_path;
     return NULL;
   }
 
@@ -187,7 +188,7 @@
   // there won't be any races.
   const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
   if (fd_flags == -1) {
-    ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
+    PLOG(ERROR) << "Failed fcntl(" << fd << ", F_GETFD)";
     return NULL;
   }
 
@@ -205,7 +206,7 @@
   //   their presence and pass them in to open().
   int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
   if (fs_flags == -1) {
-    ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
+    PLOG(ERROR) << "Failed fcntl(" << fd << ", F_GETFL)";
     return NULL;
   }
 
@@ -224,6 +225,7 @@
 bool FileDescriptorInfo::Restat() const {
   struct stat f_stat;
   if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
+    PLOG(ERROR) << "Unable to restat fd " << fd;
     return false;
   }
 
@@ -241,31 +243,31 @@
   const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
 
   if (new_fd == -1) {
-    ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
+    PLOG(ERROR) << "Failed open(" << file_path << ", " << open_flags << ")";
     return false;
   }
 
   if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
     close(new_fd);
-    ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
+    PLOG(ERROR) << "Failed fcntl(" << new_fd << ", F_SETFD, " << fd_flags << ")";
     return false;
   }
 
   if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
     close(new_fd);
-    ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
+    PLOG(ERROR) << "Failed fcntl(" << new_fd << ", F_SETFL, " << fs_flags << ")";
     return false;
   }
 
   if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
     close(new_fd);
-    ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
+    PLOG(ERROR) << "Failed lseek64(" << new_fd << ", SEEK_SET)";
     return false;
   }
 
   if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
     close(new_fd);
-    ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
+    PLOG(ERROR) << "Failed dup2(" << fd << ", " << new_fd << ")";
     return false;
   }
 
@@ -312,7 +314,10 @@
   // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
   char buf[4096];
   ssize_t len = readlink(path, buf, sizeof(buf));
-  if (len == -1) return false;
+  if (len == -1) {
+    PLOG(ERROR) << "Readlink on " << fd << " failed.";
+    return false;
+  }
 
   result->assign(buf, len);
   return true;
@@ -325,12 +330,12 @@
   socklen_t addr_len = sizeof(ss);
 
   if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
-    ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
+    PLOG(ERROR) << "Failed getsockname(" << fd << ")";
     return false;
   }
 
   if (addr->sa_family != AF_UNIX) {
-    ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
+    LOG(ERROR) << "Unsupported socket (fd=" << fd << ") with family " << addr->sa_family;
     return false;
   }
 
@@ -339,13 +344,13 @@
   size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
   // This is an unnamed local socket, we do not accept it.
   if (path_len == 0) {
-    ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
+    LOG(ERROR) << "Unsupported AF_UNIX socket (fd=" << fd << ") with empty path.";
     return false;
   }
 
   // This is a local socket with an abstract address, we do not accept it.
   if (unix_addr->sun_path[0] == '\0') {
-    ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
+    LOG(ERROR) << "Unsupported AF_UNIX socket (fd=" << fd << ") with abstract address.";
     return false;
   }
 
@@ -363,17 +368,17 @@
 bool FileDescriptorInfo::DetachSocket() const {
   const int dev_null_fd = open("/dev/null", O_RDWR);
   if (dev_null_fd < 0) {
-    ALOGE("Failed to open /dev/null : %s", strerror(errno));
+    PLOG(ERROR) << "Failed to open /dev/null";
     return false;
   }
 
   if (dup2(dev_null_fd, fd) == -1) {
-    ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
+    PLOG(ERROR) << "Failed dup2 on socket descriptor " << fd;
     return false;
   }
 
   if (close(dev_null_fd) == -1) {
-    ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
+    PLOG(ERROR) << "Failed close(" << dev_null_fd << ")";
     return false;
   }
 
@@ -384,7 +389,7 @@
 FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore) {
   DIR* d = opendir(kFdPath);
   if (d == NULL) {
-    ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+    PLOG(ERROR) << "Unable to open directory " << std::string(kFdPath);
     return NULL;
   }
   int dir_fd = dirfd(d);
@@ -397,14 +402,14 @@
       continue;
     }
     if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
-      ALOGI("Ignoring open file descriptor %d", fd);
+      LOG(INFO) << "Ignoring open file descriptor " << fd;
       continue;
     }
 
     FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
     if (info == NULL) {
       if (closedir(d) == -1) {
-        ALOGE("Unable to close directory : %s", strerror(errno));
+        PLOG(ERROR) << "Unable to close directory";
       }
       return NULL;
     }
@@ -412,7 +417,7 @@
   }
 
   if (closedir(d) == -1) {
-    ALOGE("Unable to close directory : %s", strerror(errno));
+    PLOG(ERROR) << "Unable to close directory";
     return NULL;
   }
   return new FileDescriptorTable(open_fd_map);
@@ -424,7 +429,7 @@
   // First get the list of open descriptors.
   DIR* d = opendir(kFdPath);
   if (d == NULL) {
-    ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+    PLOG(ERROR) << "Unable to open directory " << std::string(kFdPath);
     return false;
   }
 
@@ -436,7 +441,7 @@
       continue;
     }
     if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
-      ALOGI("Ignoring open file descriptor %d", fd);
+      LOG(INFO) << "Ignoring open file descriptor " << fd;
       continue;
     }
 
@@ -444,7 +449,7 @@
   }
 
   if (closedir(d) == -1) {
-    ALOGE("Unable to close directory : %s", strerror(errno));
+    PLOG(ERROR) << "Unable to close directory";
     return false;
   }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index aea7a5d..5bfcda3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1285,7 +1285,7 @@
     <permission android:name="android.permission.SCORE_NETWORKS"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @SystemApi Allows applications to request network
+    <!-- Allows applications to request network
          recommendations and scores from the NetworkScoreService.
          <p>Not for use by third-party applications. @hide -->
     <permission android:name="android.permission.REQUEST_NETWORK_SCORES"
diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml
index b910211..fd5154a 100644
--- a/core/res/res/layout/notification_template_material_messaging.xml
+++ b/core/res/res/layout/notification_template_material_messaging.xml
@@ -24,7 +24,7 @@
     <LinearLayout
             android:id="@+id/notification_action_list_margin_target"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
+            android:layout_height="wrap_content"
             android:layout_gravity="top"
             android:layout_marginTop="@dimen/notification_content_margin_top"
             android:clipToPadding="false"
@@ -38,6 +38,7 @@
             android:paddingStart="@dimen/notification_content_margin_start"
             android:paddingEnd="@dimen/notification_content_margin_end"
             android:minHeight="@dimen/notification_min_content_height"
+            android:layout_marginBottom="@dimen/notification_content_margin_bottom"
             android:clipToPadding="false"
             android:orientation="vertical"
             >
@@ -49,7 +50,6 @@
                 android:id="@+id/notification_messaging"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:paddingBottom="@dimen/notification_content_margin_bottom"
                 android:spacing="@dimen/notification_messaging_spacing" >
                 <com.android.internal.widget.ImageFloatingTextView android:id="@+id/inbox_text0"
                     style="@style/Widget.Material.Notification.MessagingText"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index c137ae2..55b8884 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2304,6 +2304,39 @@
             <enum name="manual" value="2" />
         </attr>
 
+        <!-- Describes the content of a view so that a auto-fill service can fill in the appropriate
+             data. Multiple flags can be combined to mean e.g. emailAddress or postalAddress. -->
+        <attr name="autoFillHint">
+            <!-- No hint. -->
+            <flag name="none" value="0" />
+            <!-- The view contains an email address. -->
+            <flag name="emailAddress" value="0x1" />
+            <!-- The view contains a real name. -->
+            <flag name="name" value="0x2" />
+            <!-- The view contains a user name. -->
+            <flag name="username" value="0x4" />
+            <!-- The view contains a password. -->
+            <flag name="password" value="0x8" />
+            <!-- The view contains a phone number. -->
+            <flag name="phone" value="0x10" />
+            <!-- The view contains a postal address. -->
+            <flag name="postalAddress" value="0x20" />
+            <!-- The view contains a postal code. -->
+            <flag name="postalCode" value="0x40" />
+            <!-- The view contains a credit card number. -->
+            <flag name="creditCardNumber" value="0x80" />
+            <!-- The view contains a credit card security code -->
+            <flag name="creditCardSecurityCode" value="0x100" />
+            <!-- The view contains a credit card expiration date -->
+            <flag name="creditCardExpirationDate" value="0x200" />
+            <!-- The view contains the month a credit card expires -->
+            <flag name="creditCardExpirationMonth" value="0x400" />
+            <!-- The view contains the year a credit card expires -->
+            <flag name="creditCardExpirationYear" value="0x800" />
+            <!-- The view contains the day a credit card expires -->
+            <flag name="creditCardExpirationDay" value="0x1000" />
+        </attr>
+
         <!-- Boolean that controls whether a view can take focus while in touch mode.
              If this is true for a view, that view can gain focus when clicked on, and can keep
              focus if another view is clicked on that doesn't have this attribute set to true. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 76f4a76..deacc24b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1534,6 +1534,14 @@
              of Android higher than the number given here, the permission will not
              be requested.  -->
         <attr name="maxSdkVersion" format="integer" />
+        <!-- Optional: the system must support this feature for the permission to be
+        requested.  If it doesn't support the feature, it will be as if the manifest didn't
+        request it at all. -->
+        <attr name="requiredFeature" format="string" />
+        <!-- Optional: the system must NOT support this feature for the permission to be
+        requested.  If it does support the feature, it will be as if the manifest didn't
+        request it at all. -->
+        <attr name="requiredNotFeature" format="string" />
     </declare-styleable>
 
     <!-- The <code>uses-configuration</code> tag specifies
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 01737e7..df3962c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2797,6 +2797,11 @@
         <public name="fontProviderAuthority" />
         <public name="fontProviderQuery" />
         <public name="autoFillMode" />
+        <public name="primaryContentAlpha" />
+        <public name="secondaryContentAlpha" />
+        <public name="requiredFeature" />
+        <public name="requiredNotFeature" />
+        <public name="autoFillHint" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
@@ -2806,9 +2811,6 @@
         <public name="textAssist" />
     </public-group>
 
-    <public type="attr" name="primaryContentAlpha" />
-    <public type="attr" name="secondaryContentAlpha" />
-
 
   <!-- ===============================================================
        DO NOT ADD UN-GROUPED ITEMS HERE
diff --git a/core/tests/coretests/src/android/graphics/drawable/IconTest.java b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
index cf132890..b50955b 100644
--- a/core/tests/coretests/src/android/graphics/drawable/IconTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/IconTest.java
@@ -108,13 +108,13 @@
     }
 
     @SmallTest
-    public void testWithMaskableBitmap() throws Exception {
+    public void testWithAdaptiveBitmap() throws Exception {
         final Bitmap bm1 = Bitmap.createBitmap(150, 150, Bitmap.Config.ARGB_8888);
 
         final Canvas can1 = new Canvas(bm1);
         can1.drawColor(0xFFFF0000);
 
-        final Icon im1 = Icon.createWithMaskableBitmap(bm1);
+        final Icon im1 = Icon.createWithAdaptiveBitmap(bm1);
 
         final AdaptiveIconDrawable draw1 = (AdaptiveIconDrawable) im1.loadDrawable(mContext);
 
@@ -132,12 +132,12 @@
         L("writing temp bitmaps to %s...", dir);
 
         bm1.compress(Bitmap.CompressFormat.PNG, 100,
-            new FileOutputStream(new File(dir, "maskable-bitmap1-original.png")));
+            new FileOutputStream(new File(dir, "adaptive-bitmap1-original.png")));
         test1.compress(Bitmap.CompressFormat.PNG, 100,
-            new FileOutputStream(new File(dir, "maskable-bitmap1-test.png")));
+            new FileOutputStream(new File(dir, "adaptive-bitmap1-test.png")));
         if (!equalBitmaps(bm1, test1, draw1.getSafeZone())) {
             findBitmapDifferences(bm1, test1);
-            fail("maskable bitmap1 differs, check " + dir);
+            fail("adaptive bitmap1 differs, check " + dir);
         }
     }
 
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index ff1312a..8ce7ed0 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -68,7 +68,7 @@
     /** @hide */
     public static final int TYPE_URI      = 4;
     /** @hide */
-    public static final int TYPE_BITMAP_MASKABLE      = 5;
+    public static final int TYPE_ADAPTIVE_BITMAP = 5;
 
     private static final int VERSION_STREAM_SERIALIZER = 1;
 
@@ -103,7 +103,7 @@
      * {@link #TYPE_RESOURCE},
      * {@link #TYPE_DATA}, or
      * {@link #TYPE_URI}.
-     * {@link #TYPE_BITMAP_MASKABLE}
+     * {@link #TYPE_ADAPTIVE_BITMAP}
      * @hide
      */
     public int getType() {
@@ -115,7 +115,7 @@
      * @hide
      */
     public Bitmap getBitmap() {
-        if (mType != TYPE_BITMAP && mType != TYPE_BITMAP_MASKABLE) {
+        if (mType != TYPE_BITMAP && mType != TYPE_ADAPTIVE_BITMAP) {
             throw new IllegalStateException("called getBitmap() on " + this);
         }
         return (Bitmap) mObj1;
@@ -221,7 +221,7 @@
     private static final String typeToString(int x) {
         switch (x) {
             case TYPE_BITMAP: return "BITMAP";
-            case TYPE_BITMAP_MASKABLE: return "BITMAP_MASKABLE";
+            case TYPE_ADAPTIVE_BITMAP: return "BITMAP_MASKABLE";
             case TYPE_DATA: return "DATA";
             case TYPE_RESOURCE: return "RESOURCE";
             case TYPE_URI: return "URI";
@@ -289,7 +289,7 @@
         switch (mType) {
             case TYPE_BITMAP:
                 return new BitmapDrawable(context.getResources(), getBitmap());
-            case TYPE_BITMAP_MASKABLE:
+            case TYPE_ADAPTIVE_BITMAP:
                 return new AdaptiveIconDrawable(null,
                     new BitmapDrawable(context.getResources(), getBitmap()));
             case TYPE_RESOURCE:
@@ -395,7 +395,7 @@
      * @hide
      */
     public void convertToAshmem() {
-        if ((mType == TYPE_BITMAP || mType == TYPE_BITMAP_MASKABLE) &&
+        if ((mType == TYPE_BITMAP || mType == TYPE_ADAPTIVE_BITMAP) &&
             getBitmap().isMutable() &&
             getBitmap().getAllocationByteCount() >= MIN_ASHMEM_ICON_SIZE) {
             setBitmap(getBitmap().createAshmemBitmap());
@@ -416,7 +416,7 @@
 
         switch (mType) {
             case TYPE_BITMAP:
-            case TYPE_BITMAP_MASKABLE:
+            case TYPE_ADAPTIVE_BITMAP:
                 getBitmap().compress(Bitmap.CompressFormat.PNG, 100, dataStream);
                 break;
             case TYPE_DATA:
@@ -452,8 +452,8 @@
             switch (type) {
                 case TYPE_BITMAP:
                     return createWithBitmap(BitmapFactory.decodeStream(inputStream));
-                case TYPE_BITMAP_MASKABLE:
-                    return createWithMaskableBitmap(BitmapFactory.decodeStream(inputStream));
+                case TYPE_ADAPTIVE_BITMAP:
+                    return createWithAdaptiveBitmap(BitmapFactory.decodeStream(inputStream));
                 case TYPE_DATA:
                     final int length = inputStream.readInt();
                     final byte[] data = new byte[length];
@@ -488,7 +488,7 @@
         }
         switch (mType) {
             case TYPE_BITMAP:
-            case TYPE_BITMAP_MASKABLE:
+            case TYPE_ADAPTIVE_BITMAP:
                 return getBitmap() == otherIcon.getBitmap();
             case TYPE_DATA:
                 return getDataLength() == otherIcon.getDataLength()
@@ -566,11 +566,11 @@
      * by {@link AdaptiveIconDrawable}.
      * @param bits A valid {@link android.graphics.Bitmap} object
      */
-    public static Icon createWithMaskableBitmap(Bitmap bits) {
+    public static Icon createWithAdaptiveBitmap(Bitmap bits) {
         if (bits == null) {
             throw new IllegalArgumentException("Bitmap must not be null.");
         }
-        final Icon rep = new Icon(TYPE_BITMAP_MASKABLE);
+        final Icon rep = new Icon(TYPE_ADAPTIVE_BITMAP);
         rep.setBitmap(bits);
         return rep;
     }
@@ -679,7 +679,7 @@
         final StringBuilder sb = new StringBuilder("Icon(typ=").append(typeToString(mType));
         switch (mType) {
             case TYPE_BITMAP:
-            case TYPE_BITMAP_MASKABLE:
+            case TYPE_ADAPTIVE_BITMAP:
                 sb.append(" size=")
                         .append(getBitmap().getWidth())
                         .append("x")
@@ -718,7 +718,7 @@
      * Parcelable interface
      */
     public int describeContents() {
-        return (mType == TYPE_BITMAP || mType == TYPE_BITMAP_MASKABLE || mType == TYPE_DATA)
+        return (mType == TYPE_BITMAP || mType == TYPE_ADAPTIVE_BITMAP || mType == TYPE_DATA)
                 ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
     }
 
@@ -728,7 +728,7 @@
         this(in.readInt());
         switch (mType) {
             case TYPE_BITMAP:
-            case TYPE_BITMAP_MASKABLE:
+            case TYPE_ADAPTIVE_BITMAP:
                 final Bitmap bits = Bitmap.CREATOR.createFromParcel(in);
                 mObj1 = bits;
                 break;
@@ -767,7 +767,7 @@
         dest.writeInt(mType);
         switch (mType) {
             case TYPE_BITMAP:
-            case TYPE_BITMAP_MASKABLE:
+            case TYPE_ADAPTIVE_BITMAP:
                 final Bitmap bits = getBitmap();
                 getBitmap().writeToParcel(dest, flags);
                 break;
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index b685431..c4bb72c 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -29,8 +29,8 @@
     byte[] getCertificate(String alias);
     byte[] getCaCertificates(String alias);
 
-    // APIs used by CertInstaller
-    void installCaCertificate(in byte[] caCertificate);
+    // APIs used by CertInstaller and DevicePolicyManager
+    String installCaCertificate(in byte[] caCertificate);
 
     // APIs used by DevicePolicyManager
     boolean installKeyPair(in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias);
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index bf7a779a..d6b1cf1 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -45,7 +45,6 @@
                 PURPOSE_DECRYPT,
                 PURPOSE_SIGN,
                 PURPOSE_VERIFY,
-                PURPOSE_WRAP_KEY,
                 })
     public @interface PurposeEnum {}
 
@@ -70,12 +69,6 @@
     public static final int PURPOSE_VERIFY = 1 << 3;
 
     /**
-     * Purpose of key: wrapping key for secure key import.
-     */
-    public static final int PURPOSE_WRAP_KEY = 1 << 4;
-
-
-    /**
      * @hide
      */
     public static abstract class Purpose {
@@ -91,8 +84,6 @@
                     return KeymasterDefs.KM_PURPOSE_SIGN;
                 case PURPOSE_VERIFY:
                     return KeymasterDefs.KM_PURPOSE_VERIFY;
-                case PURPOSE_WRAP_KEY:
-                    return KeymasterDefs.KM_PURPOSE_WRAP_KEY;
                 default:
                     throw new IllegalArgumentException("Unknown purpose: " + purpose);
             }
@@ -108,8 +99,6 @@
                     return PURPOSE_SIGN;
                 case KeymasterDefs.KM_PURPOSE_VERIFY:
                     return PURPOSE_VERIFY;
-                case KeymasterDefs.KM_PURPOSE_WRAP_KEY:
-                    return PURPOSE_WRAP_KEY;
                 default:
                     throw new IllegalArgumentException("Unknown purpose: " + purpose);
             }
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 35ff635..1b57e29 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -78,7 +78,7 @@
     // Render all layers to be updated, in order. Defer in reverse order, so that they'll be
     // updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse)
     for (int i = layers.entries().size() - 1; i >= 0; i--) {
-        RenderNode* layerNode = layers.entries()[i].renderNode;
+        RenderNode* layerNode = layers.entries()[i].renderNode.get();
         // only schedule repaint if node still on layer - possible it may have been
         // removed during a dropped frame, but layers may still remain scheduled so
         // as not to lose info on what portion is damaged
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
index 5b1a854..38f3596 100644
--- a/libs/hwui/LayerUpdateQueue.h
+++ b/libs/hwui/LayerUpdateQueue.h
@@ -19,6 +19,7 @@
 
 #include "Rect.h"
 #include "utils/Macros.h"
+#include <utils/StrongPointer.h>
 
 #include <vector>
 #include <unordered_map>
@@ -35,7 +36,7 @@
         Entry(RenderNode* renderNode, const Rect& damage)
                 : renderNode(renderNode)
                 , damage(damage) {}
-        RenderNode* renderNode;
+        sp<RenderNode> renderNode;
         Rect damage;
     };
 
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 2e33609..374c1b1 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -268,7 +268,7 @@
 
 // Geometry
 void RecordingCanvas::drawPoints(const float* points, int floatCount, const SkPaint& paint) {
-    if (CC_UNLIKELY(floatCount < 2 || PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(floatCount < 2 || paint.nothingToDraw())) return;
     floatCount &= ~0x1; // round down to nearest two
 
     addOp(alloc().create_trivial<PointsOp>(
@@ -279,7 +279,7 @@
 }
 
 void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPaint& paint) {
-    if (CC_UNLIKELY(floatCount < 4 || PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(floatCount < 4 || paint.nothingToDraw())) return;
     floatCount &= ~0x3; // round down to nearest four
 
     addOp(alloc().create_trivial<LinesOp>(
@@ -290,7 +290,7 @@
 }
 
 void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
-    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
 
     addOp(alloc().create_trivial<RectOp>(
             Rect(left, top, right, bottom),
@@ -333,7 +333,7 @@
 }
 
 void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
-    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
 
     if (paint.getStyle() == SkPaint::kFill_Style
             && (!paint.isAntiAlias() || mState.currentTransform()->isSimple())) {
@@ -362,7 +362,7 @@
 
 void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom,
             float rx, float ry, const SkPaint& paint) {
-    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
 
     if (CC_LIKELY(MathUtils::isPositive(rx) || MathUtils::isPositive(ry))) {
         addOp(alloc().create_trivial<RoundRectOp>(
@@ -398,7 +398,7 @@
 
 void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
     // TODO: move to Canvas.h
-    if (CC_UNLIKELY(radius <= 0 || PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
 
     drawOval(x - radius, y - radius, x + radius, y + radius, paint);
 }
@@ -419,7 +419,7 @@
 }
 
 void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
-    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
 
     addOp(alloc().create_trivial<OvalOp>(
             Rect(left, top, right, bottom),
@@ -430,7 +430,7 @@
 
 void RecordingCanvas::drawArc(float left, float top, float right, float bottom,
         float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
-    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
 
     if (fabs(sweepAngle) >= 360.0f) {
         drawOval(left, top, right, bottom, paint);
@@ -445,7 +445,7 @@
 }
 
 void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
-    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
 
     addOp(alloc().create_trivial<PathOp>(
             Rect(path.getBounds()),
@@ -543,7 +543,7 @@
 void RecordingCanvas::drawGlyphs(const uint16_t* glyphs, const float* positions, int glyphCount,
             const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
             float boundsRight, float boundsBottom, float totalAdvance) {
-    if (!glyphs || !positions || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
+    if (!glyphs || !positions || glyphCount <= 0 || paint.nothingToDraw()) return;
     glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
     positions = refBuffer<float>(positions, glyphCount * 2);
 
@@ -563,7 +563,7 @@
         glyphs[0] = layout.getGlyphId(i);
         float x = hOffset + layout.getX(i);
         float y = vOffset + layout.getY(i);
-        if (PaintUtils::paintWillNotDrawText(paint)) return;
+        if (paint.nothingToDraw()) return;
         const uint16_t* tempGlyphs = refBuffer<glyph_t>(glyphs, 1);
         addOp(alloc().create_trivial<TextOnPathOp>(
                 *(mState.currentSnapshot()->transform),
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index c57b1b3..51a7d00 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -443,7 +443,7 @@
 
 void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint,
                             SkCanvas::PointMode mode) {
-    if (CC_UNLIKELY(count < 2 || PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(count < 2 || paint.nothingToDraw())) return;
     // convert the floats into SkPoints
     count >>= 1;    // now it is the number of points
     std::unique_ptr<SkPoint[]> pts(new SkPoint[count]);
@@ -469,49 +469,49 @@
 }
 
 void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
-    if (CC_UNLIKELY(count < 4 || PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(count < 4 || paint.nothingToDraw())) return;
     this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
 }
 
 void SkiaCanvas::drawRect(float left, float top, float right, float bottom,
         const SkPaint& paint) {
-    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
     mCanvas->drawRectCoords(left, top, right, bottom, paint);
 
 }
 
 void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
-    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
     mCanvas->drawRegion(region, paint);
 }
 
 void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom,
         float rx, float ry, const SkPaint& paint) {
-    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
     mCanvas->drawRoundRect(rect, rx, ry, paint);
 }
 
 void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
-    if (CC_UNLIKELY(radius <= 0 || PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
     mCanvas->drawCircle(x, y, radius, paint);
 }
 
 void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
-    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
     mCanvas->drawOval(oval, paint);
 }
 
 void SkiaCanvas::drawArc(float left, float top, float right, float bottom,
         float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
-    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
     mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, paint);
 }
 
 void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
-    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect rect;
     SkRRect roundRect;
     if (path.isOval(&rect)) {
@@ -698,7 +698,7 @@
         const SkPaint& paint, float x, float y,
         float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
         float totalAdvance) {
-     if (!text || !positions || count <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
+     if (!text || !positions || count <= 0 || paint.nothingToDraw()) return;
     // Set align to left for drawing, as we don't want individual
     // glyphs centered or right-aligned; the offset above takes
     // care of all alignment.
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 117395b..39f11b8 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -197,12 +197,12 @@
             if (needsLayer) {
                 canvas->saveLayer(bounds, &paint);
             }
-            canvas->drawDrawable(displayList->mDrawable.get());
+            displayList->draw(canvas);
             if (needsLayer) {
                 canvas->restore();
             }
         } else {
-            canvas->drawDrawable(displayList->mDrawable.get());
+            displayList->draw(canvas);
         }
     }
 }
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index b4babcb..496f7ba 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -27,10 +27,6 @@
 namespace uirenderer {
 namespace skiapipeline {
 
-SkiaDisplayList::SkiaDisplayList(SkRect bounds) : mDrawable(SkLiteDL::New(bounds)) {
-    SkASSERT(projectionReceiveIndex == -1);
-}
-
 void SkiaDisplayList::syncContents() {
     for (auto& functor : mChildFunctors) {
         functor.syncFunctor();
@@ -41,7 +37,7 @@
 }
 
 bool SkiaDisplayList::reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) {
-    reset(SkRect::MakeEmpty());
+    reset();
     node->attachAvailableList(this);
     return true;
 }
@@ -102,10 +98,10 @@
     return isDirty;
 }
 
-void SkiaDisplayList::reset(SkRect bounds) {
+void SkiaDisplayList::reset() {
     mProjectionReceiver = nullptr;
 
-    mDrawable->reset(bounds);
+    mDisplayList.reset();
 
     mMutableImages.clear();
     mVectorDrawables.clear();
@@ -119,7 +115,7 @@
 
 void SkiaDisplayList::output(std::ostream& output, uint32_t level) {
     DumpOpsCanvas canvas(output, level, *this);
-    mDrawable->draw(&canvas, nullptr);
+    mDisplayList.draw(&canvas);
 }
 
 }; // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 439b999..6ee5922 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -22,7 +22,7 @@
 
 #include <deque>
 #include <SkLiteDL.h>
-#include <SkPictureRecorder.h>
+#include <SkLiteRecorder.h>
 
 namespace android {
 namespace uirenderer {
@@ -39,22 +39,22 @@
  */
 class SkiaDisplayList : public DisplayList {
 public:
-    SkiaDisplayList(SkRect bounds);
+    SkiaDisplayList() { SkASSERT(projectionReceiveIndex == -1); }
     virtual ~SkiaDisplayList() {
         /* Given that we are using a LinearStdAllocator to store some of the
          * SkDrawable contents we must ensure that any other object that is
          * holding a reference to those drawables is destroyed prior to their
          * deletion.
          */
-        mDrawable.reset();
+        mDisplayList.reset();
     }
 
     /**
      * This resets the DisplayList so that it behaves as if the object were newly
-     * constructed with the provided bounds.  The reuse avoids any overhead
-     * associated with destroying the SkLiteDL as well as the deques and vectors.
+     * constructed.  The reuse avoids any overhead associated with destroying
+     * the SkLiteDL as well as the deques and vectors.
      */
-    void reset(SkRect bounds);
+    void reset();
 
     /**
      * Use the linear allocator to create any SkDrawables needed by the display
@@ -72,7 +72,7 @@
     /**
      * Returns true if the DisplayList does not have any recorded content
      */
-    bool isEmpty() const override { return mDrawable->empty(); }
+    bool isEmpty() const override { return mDisplayList.empty(); }
 
     /**
      * Returns true if this list directly contains a GLFunctor drawing command.
@@ -126,18 +126,24 @@
      */
     inline bool containsProjectionReceiver() const { return mProjectionReceiver; }
 
+    void attachRecorder(SkLiteRecorder* recorder, const SkIRect& bounds) {
+        recorder->reset(&mDisplayList, bounds);
+    }
+
+    void draw(SkCanvas* canvas) { mDisplayList.draw(canvas); }
+
     void output(std::ostream& output, uint32_t level) override;
 
     /**
      * We use std::deque here because (1) we need to iterate through these
-     * elements and (2) mDrawable holds pointers to the elements, so they cannot
-     * relocate.
+     * elements and (2) mDisplayList holds pointers to the elements, so they
+     * cannot relocate.
      */
     std::deque<RenderNodeDrawable> mChildNodes;
     std::deque<GLFunctorDrawable> mChildFunctors;
     std::vector<SkImage*> mMutableImages;
     std::vector<VectorDrawableRoot*> mVectorDrawables;
-    sk_sp<SkLiteDL> mDrawable;
+    SkLiteDL mDisplayList;
 
     //mProjectionReceiver points to a child node (stored in mChildNodes) that is as a projection
     //receiver. It is set at record time and used at both prepare and draw tree traversals to
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 430d6be..11dc1f4 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -80,7 +80,7 @@
 void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
     // Render all layers that need to be updated, in order.
     for (size_t i = 0; i < layers.entries().size(); i++) {
-        RenderNode* layerNode = layers.entries()[i].renderNode;
+        RenderNode* layerNode = layers.entries()[i].renderNode.get();
         // only schedule repaint if node still on layer - possible it may have been
         // removed during a dropped frame, but layers may still remain scheduled so
         // as not to lose info on what portion is damaged
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index dbe0296..559d268 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -39,14 +39,11 @@
     if (renderNode) {
         mDisplayList = renderNode->detachAvailableList();
     }
-    SkRect bounds = SkRect::MakeWH(width, height);
-    if (mDisplayList) {
-        mDisplayList->reset(bounds);
-    } else {
-        mDisplayList.reset(new SkiaDisplayList(bounds));
+    if (!mDisplayList) {
+        mDisplayList.reset(new SkiaDisplayList());
     }
 
-    mRecorder.reset(mDisplayList->mDrawable.get());
+    mDisplayList->attachRecorder(&mRecorder, SkIRect::MakeWH(width, height));
     SkiaCanvas::reset(&mRecorder);
 }
 
diff --git a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
index 4db1cb9..91c7514 100644
--- a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
+++ b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
@@ -48,11 +48,11 @@
 
     EXPECT_EQ(3u, queue.entries().size());
 
-    EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
+    EXPECT_EQ(a.get(), queue.entries()[0].renderNode.get());
     EXPECT_EQ(Rect(25, 25, 75, 75), queue.entries()[0].damage);
-    EXPECT_EQ(b.get(), queue.entries()[1].renderNode);
+    EXPECT_EQ(b.get(), queue.entries()[1].renderNode.get());
     EXPECT_EQ(Rect(100, 100, 200, 200), queue.entries()[1].damage); // clipped to bounds
-    EXPECT_EQ(c.get(), queue.entries()[2].renderNode);
+    EXPECT_EQ(c.get(), queue.entries()[2].renderNode.get());
     EXPECT_EQ(Rect(0, 0, 1, 1), queue.entries()[2].damage); // rounded out
 }
 
@@ -65,7 +65,7 @@
 
     EXPECT_EQ(1u, queue.entries().size());
 
-    EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
+    EXPECT_EQ(a.get(), queue.entries()[0].renderNode.get());
     EXPECT_EQ(Rect(10, 10, 40, 40), queue.entries()[0].damage);
 }
 
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index f21b3f7..cbea501 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -44,9 +44,9 @@
                 canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
             });
 
-    auto skLiteDL = SkLiteDL::New(SkRect::MakeWH(1, 1));
+    SkLiteDL skLiteDL;
     SkLiteRecorder canvas;
-    canvas.reset(skLiteDL.get());
+    canvas.reset(&skLiteDL, SkIRect::MakeWH(1, 1));
     canvas.translate(100, 100);
     RenderNodeDrawable drawable(rootNode.get(), &canvas);
 
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 2925243..eda4a9d 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -331,7 +331,7 @@
     // damage rect.
     EXPECT_TRUE(rootNode->getDisplayList()->hasVectorDrawables());
     EXPECT_FALSE(info.layerUpdateQueue->entries().empty());
-    EXPECT_EQ(rootNode.get(), info.layerUpdateQueue->entries().at(0).renderNode);
+    EXPECT_EQ(rootNode.get(), info.layerUpdateQueue->entries().at(0).renderNode.get());
     EXPECT_EQ(uirenderer::Rect(0, 0, 200, 400), info.layerUpdateQueue->entries().at(0).damage);
     canvasContext->destroy();
 }
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 0ac09ac..7fb75dc 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -18,6 +18,7 @@
 
 #include <gtest/gtest.h>
 #include <RecordingCanvas.h>
+#include <SkBlurDrawLooper.h>
 #include <SkPicture.h>
 #include <SkPictureRecorder.h>
 
@@ -59,3 +60,21 @@
     EXPECT_EQ(directOp->unmappedBounds, pictureOp->unmappedBounds);
     EXPECT_EQ(directOp->localMatrix, pictureOp->localMatrix);
 }
+
+TEST(SkiaCanvas, drawShadowLayer) {
+    auto surface = SkSurface::MakeRasterN32Premul(10, 10);
+    SkiaCanvas canvas(surface->getCanvas());
+
+    // clear to white
+    canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrc);
+
+    SkPaint paint;
+    // it is transparent to ensure that we still draw the rect since it has a looper
+    paint.setColor(SK_ColorTRANSPARENT);
+    // this is how view's shadow layers are implemented
+    paint.setLooper(SkBlurDrawLooper::Make(0xF0000000, 6.0f, 0, 10));
+    canvas.drawRect(3, 3, 7, 7, paint);
+
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
+    ASSERT_NE(TestUtils::getColor(surface, 5, 5), SK_ColorWHITE);
+}
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index be460bf..dd8f4b4 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -30,16 +30,13 @@
 using namespace android::uirenderer::skiapipeline;
 
 TEST(SkiaDisplayList, create) {
-    SkRect bounds = SkRect::MakeWH(200, 200);
-    SkiaDisplayList skiaDL(bounds);
+    SkiaDisplayList skiaDL;
     ASSERT_TRUE(skiaDL.isEmpty());
     ASSERT_FALSE(skiaDL.mProjectionReceiver);
-    ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds);
 }
 
 TEST(SkiaDisplayList, reset) {
-    SkRect bounds = SkRect::MakeWH(200, 200);
-    SkiaDisplayList skiaDL(bounds);
+    SkiaDisplayList skiaDL;
 
     SkCanvas dummyCanvas;
     RenderNodeDrawable drawable(nullptr, &dummyCanvas);
@@ -47,10 +44,9 @@
     skiaDL.mChildFunctors.emplace_back(nullptr, nullptr, &dummyCanvas);
     skiaDL.mMutableImages.push_back(nullptr);
     skiaDL.mVectorDrawables.push_back(nullptr);
-    skiaDL.mDrawable->drawAnnotation(bounds, "testAnnotation", nullptr);
+    skiaDL.mDisplayList.drawAnnotation(SkRect::MakeWH(200, 200), "testAnnotation", nullptr);
     skiaDL.mProjectionReceiver = &drawable;
 
-    ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds);
     ASSERT_FALSE(skiaDL.mChildNodes.empty());
     ASSERT_FALSE(skiaDL.mChildFunctors.empty());
     ASSERT_FALSE(skiaDL.mMutableImages.empty());
@@ -58,10 +54,8 @@
     ASSERT_FALSE(skiaDL.isEmpty());
     ASSERT_TRUE(skiaDL.mProjectionReceiver);
 
-    bounds = SkRect::MakeWH(100, 100);
-    skiaDL.reset(bounds);
+    skiaDL.reset();
 
-    ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds);
     ASSERT_TRUE(skiaDL.mChildNodes.empty());
     ASSERT_TRUE(skiaDL.mChildFunctors.empty());
     ASSERT_TRUE(skiaDL.mMutableImages.empty());
@@ -79,7 +73,7 @@
     ASSERT_EQ(availableList.get(), nullptr);
 
     // attach a displayList for reuse
-    SkiaDisplayList skiaDL(SkRect::MakeWH(200, 200));
+    SkiaDisplayList skiaDL;
     ASSERT_TRUE(skiaDL.reuseDisplayList(renderNode.get(), nullptr));
 
     // detach the list that you just attempted to reuse
@@ -93,13 +87,13 @@
 }
 
 TEST(SkiaDisplayList, syncContexts) {
-    SkRect bounds = SkRect::MakeWH(200, 200);
-    SkiaDisplayList skiaDL(bounds);
+    SkiaDisplayList skiaDL;
 
     SkCanvas dummyCanvas;
     TestUtils::MockFunctor functor;
     skiaDL.mChildFunctors.emplace_back(&functor, nullptr, &dummyCanvas);
 
+    SkRect bounds = SkRect::MakeWH(200, 200);
     VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
     vectorDrawable.mutateStagingProperties()->setBounds(bounds);
     skiaDL.mVectorDrawables.push_back(&vectorDrawable);
@@ -127,7 +121,7 @@
     DamageAccumulator damageAccumulator;
     info.damageAccumulator = &damageAccumulator;
 
-    SkiaDisplayList skiaDL(SkRect::MakeWH(200, 200));
+    SkiaDisplayList skiaDL;
 
     // prepare with a clean VD
     VectorDrawableRoot cleanVD(new VectorDrawable::Group());
@@ -170,8 +164,7 @@
 }
 
 TEST(SkiaDisplayList, updateChildren) {
-    SkRect bounds = SkRect::MakeWH(200, 200);
-    SkiaDisplayList skiaDL(bounds);
+    SkiaDisplayList skiaDL;
 
     sp<RenderNode> renderNode = new RenderNode();
     SkCanvas dummyCanvas;
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index 845a3ea..2673be1c 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -39,21 +39,6 @@
         return GL_NEAREST;
     }
 
-    // TODO: move to a method on android:Paint? replace with SkPaint::nothingToDraw()?
-    static inline bool paintWillNotDraw(const SkPaint& paint) {
-        return paint.getAlpha() == 0
-                && !paint.getColorFilter()
-                && paint.getBlendMode() == SkBlendMode::kSrcOver;
-    }
-
-    // TODO: move to a method on android:Paint? replace with SkPaint::nothingToDraw()?
-    static inline bool paintWillNotDrawText(const SkPaint& paint) {
-        return paint.getAlpha() == 0
-                && paint.getLooper() == nullptr
-                && !paint.getColorFilter()
-                && paint.getBlendMode() == SkBlendMode::kSrcOver;
-    }
-
     static bool isOpaquePaint(const SkPaint* paint) {
         if (!paint) return true; // default (paintless) behavior is SrcOver, black
 
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 1ca9658..d264127 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -3188,9 +3188,55 @@
     /**
      *  Returns Analytics/Metrics data about the current content being
      *
-     * @return a Bundle containint the set of attributes and values available
+     * @return a Bundle containing the set of attributes and values available
      * for the media being handled by this instance of MediaCodec
      *
+     *  <table style="width: 0%">
+     *   <thead>
+     *    <tr>
+     *     <th>Key</th>
+     *     <th>Type</th>
+     *     <th>Description</th>
+     *    </tr>
+     *   </thead>
+     *   <tbody>
+     *    <tr>
+     *     <td>{@code "codec"}</td>
+     *     <td>String</td>
+     *     <td>Identifies the particular codec in use</td>
+     *    </tr><tr>
+     *     <td>{@code "mime"}</td>
+     *     <td>String</td>
+     *     <td>Mime type of the media being encoded/decoded</td>
+     *    </tr><tr>
+     *     <td>{@code "mode"}</td>
+     *     <td>String</td>
+     *     <td>"Audio" or "Video"</td>
+     *    </tr><tr>
+     *     <td>{@code "secure"}</td>
+     *     <td>Integer</td>
+     *     <td>Indicates whether the code is operating on secure content and
+     *         may also use capabilities in android.media.MediaCrypto</td>
+     *    </tr><tr>
+     *     <td>{@code "height"}</td>
+     *     <td>Integer</td>
+     *     <td>Height (pixels); valid only when mode=video</td>
+     *    </tr><tr>
+     *     <td>{@code "width"}</td>
+     *     <td>Integer</td>
+     *     <td>Width (pixels); valid only when mode=video</td>
+     *    </tr><tr>
+     *     <td>{@code "rotation"}</td>
+     *     <td>Integer</td>
+     *     <td>rotation (degrees) to orient the video onto the target surface;
+     *         valid only when mode=video. Note there may be additional
+     *         rotations applied when the surface is mapped to the screen.</td>
+     *    </tr>
+     *   </tbody>
+     *  </table>
+     *
+     *  Additional fields specific to individual codecs will also appear in
+     *  the return value.
      */
     public native Bundle getMetrics();
 
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 01ae36ff..b9e409d 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -656,6 +656,33 @@
      * @return the set of keys and values available for the media being
      * handled by this instance of MediaExtractor
      *
+     *  <table style="width: 0%">
+     *   <thead>
+     *    <tr>
+     *     <th>Key</th>
+     *     <th>Type</th>
+     *     <th>Description</th>
+     *    </tr>
+     *   </thead>
+     *   <tbody>
+     *    <tr>
+     *     <td>{@code "fmt"}</td>
+     *     <td>String</td>
+     *     <td>The container format (which determines the handler)</td>
+     *    </tr><tr>
+     *     <td>{@code "mime"}</td>
+     *     <td>String</td>
+     *     <td>Mime type of the container.</td>
+     *    </tr><tr>
+     *     <td>{@code "ntrk"}</td>
+     *     <td>Integer</td>
+     *     <td>Number of tracks in the container</td>
+     *    </tr>
+     *   </tbody>
+     *  </table>
+     *
+     *  Additional fields specific to individual codecs will also appear in
+     *  the return value.
      */
     public native Bundle getMetrics();
 
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index fb37f9f..b85c911 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1498,6 +1498,64 @@
      *
      * @return the a map of attributes and values available for this video
      * player or null if no metrics are available.
+     *
+     *  <table style="width: 0%">
+     *   <thead>
+     *    <tr>
+     *     <th>Key</th>
+     *     <th>Type</th>
+     *     <th>Description</th>
+     *    </tr>
+     *   </thead>
+     *   <tbody>
+     *    <tr>
+     *     <td>{@code "video/codec"}</td>
+     *     <td>String</td>
+     *     <td>Identifies the video codec in use</td>
+     *    </tr><tr>
+     *     <td>{@code "video/mime"}</td>
+     *     <td>String</td>
+     *     <td>Mime type of the video being encoded/decoded</td>
+     *    </tr><tr>
+     *     <td>{@code "audio/codec"}</td>
+     *     <td>String</td>
+     *     <td>Identifies the audio codec in use</td>
+     *    </tr><tr>
+     *     <td>{@code "audio/mime"}</td>
+     *     <td>String</td>
+     *     <td>Mime type of the audio being encoded/decoded</td>
+     *    </tr><tr>
+     *     <td>{@code "ht"}</td>
+     *     <td>Integer</td>
+     *     <td>Height (pixels); valid only when mode=video</td>
+     *    </tr><tr>
+     *     <td>{@code "wid"}</td>
+     *     <td>Integer</td>
+     *     <td>Width (pixels); valid only when mode=video</td>
+     *    </tr><tr>
+     *     <td>{@code "frame"}</td>
+     *     <td>Integer</td>
+     *     <td>Number of decoded video frames sent to the display</td>
+     *    </tr><tr>
+     *     <td>{@code "dropped"}</td>
+     *     <td>Integer</td>
+     *     <td>Number of decoded video frames that were not sent to display.
+     *         These frames were dropped by the player.</td>
+     *    </tr><tr>
+     *     <td>{@code "durationMs"}</td>
+     *     <td>Integer</td>
+     *     <td>The length of the media being played (in ms), e.g. "This video lasts for 30000 milliseconds". </td>
+     *    </tr><tr>
+     *     <td>{@code "playingMs"}</td>
+     *     <td>Integer</td>
+     *     <td>The time the media has been played (in ms). If you watch a
+     *         30 second twice through, this will report 60000 ms.</td>
+     *    </tr>
+     *   </tbody>
+     *  </table>
+     *
+     *  Additional fields specific to individual codecs will also appear in
+     *  the return value.
      */
     public native Bundle getMetrics();
 
diff --git a/media/java/android/media/midi/MidiDevice.java b/media/java/android/media/midi/MidiDevice.java
index f79cd06..4b43260 100644
--- a/media/java/android/media/midi/MidiDevice.java
+++ b/media/java/android/media/midi/MidiDevice.java
@@ -35,6 +35,10 @@
  * Instances of this class are created by {@link MidiManager#openDevice}.
  */
 public final class MidiDevice implements Closeable {
+    static {
+        System.loadLibrary("media_jni");
+    }
+
     private static final String TAG = "MidiDevice";
 
     private final MidiDeviceInfo mDeviceInfo;
@@ -43,6 +47,7 @@
     private final IBinder mClientToken;
     private final IBinder mDeviceToken;
     private boolean mIsDeviceClosed;
+    private boolean mIsMirroredToNative;
 
     private final CloseGuard mGuard = CloseGuard.get();
 
@@ -209,10 +214,45 @@
         }
     }
 
+    /**
+     * Makes Midi Device available to the Native API
+     * @hide
+     */
+    public void mirrorToNative() throws IOException {
+        if (mIsDeviceClosed || mIsMirroredToNative) {
+            return;
+        }
+
+        int result = mirrorToNative(mDeviceServer.asBinder(), mDeviceInfo.getId());
+        if (result != 0) {
+            throw new IOException("Failed mirroring to native: " + result);
+        }
+
+        mIsMirroredToNative = true;
+    }
+
+    /**
+     * Makes Midi Device no longer available to the Native API
+     * @hide
+     */
+    public void removeFromNative() throws IOException {
+        if (!mIsMirroredToNative) {
+            return;
+        }
+
+        int result = removeFromNative(mDeviceInfo.getId());
+        if (result != 0) {
+            throw new IOException("Failed removing from native: " + result);
+        }
+
+        mIsMirroredToNative = false;
+    }
+
     @Override
     public void close() throws IOException {
         synchronized (mGuard) {
             if (!mIsDeviceClosed) {
+                removeFromNative();
                 mGuard.close();
                 mIsDeviceClosed = true;
                 try {
@@ -238,4 +278,7 @@
     public String toString() {
         return ("MidiDevice: " + mDeviceInfo.toString());
     }
+
+    private native int mirrorToNative(IBinder deviceServerBinder, int uid);
+    private native int removeFromNative(int uid);
 }
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index e5af357..aee9d38e 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -776,8 +776,8 @@
         mSurface = null;
         mSurfaceView = new SurfaceView(getContext(), mAttrs, mDefStyleAttr) {
             @Override
-            protected void updateSurface() {
-                super.updateSurface();
+            protected void updateWindow() {
+                super.updateWindow();
                 relayoutSessionOverlayView();
             }};
         // The surface view's content should be treated as secure all the time.
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 861ed0a..23bf3d6 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -26,6 +26,7 @@
     android_mtp_MtpDatabase.cpp \
     android_mtp_MtpDevice.cpp \
     android_mtp_MtpServer.cpp \
+    midi/android_media_midi_MidiDevice.cpp \
 
 LOCAL_SHARED_LIBRARIES := \
     libandroid_runtime \
@@ -34,6 +35,7 @@
     libbinder \
     libmedia \
     libmediadrm \
+    libmidi \
     libskia \
     libui \
     liblog \
@@ -55,6 +57,7 @@
     external/tremor/Tremor \
     frameworks/base/core/jni \
     frameworks/base/libs/hwui \
+    frameworks/base/media/native \
     frameworks/av/media/libmedia \
     frameworks/av/media/libstagefright \
     frameworks/av/media/mtp \
diff --git a/media/jni/midi/android_media_midi_MidiDevice.cpp b/media/jni/midi/android_media_midi_MidiDevice.cpp
new file mode 100644
index 0000000..1e54bac
--- /dev/null
+++ b/media/jni/midi/android_media_midi_MidiDevice.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Midi-JNI"
+
+#include <android_util_Binder.h>
+#include <midi/MidiDeviceRegistry.h>
+#include <nativehelper/jni.h>
+#include <utils/Log.h>
+
+using namespace android;
+using namespace android::media::midi;
+
+extern "C" jint Java_android_media_midi_MidiDevice_mirrorToNative(
+        JNIEnv *env, jobject thiz, jobject midiDeviceServer, jint id)
+{
+    (void)thiz;
+    sp<IBinder> serverBinder = ibinderForJavaObject(env, midiDeviceServer);
+    if (serverBinder.get() == NULL) {
+        ALOGE("Could not obtain IBinder from passed jobject");
+        return -EINVAL;
+    }
+    // return MidiDeviceManager::getInstance().addDevice(serverBinder, uid);
+    return MidiDeviceRegistry::getInstance().addDevice(
+               new BpMidiDeviceServer(serverBinder), id);
+}
+
+extern "C" jint Java_android_media_midi_MidiDevice_removeFromNative(
+        JNIEnv *env, jobject thiz, jint uid)
+{
+    (void)env;
+    (void)thiz;
+    // return MidiDeviceManager::getInstance().removeDevice(uid);
+    return MidiDeviceRegistry::getInstance().removeDevice(uid);
+}
diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp
new file mode 100644
index 0000000..3500805
--- /dev/null
+++ b/media/native/midi/Android.bp
@@ -0,0 +1,21 @@
+// 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.
+
+// The headers module is in frameworks/media/native/midi/Android.bp.
+ndk_library {
+    name: "libmidi.ndk",
+    symbol_file: "libmidi.map.txt",
+    first_version: "26",
+//    unversioned_until: "current",
+}
diff --git a/media/native/midi/Android.mk b/media/native/midi/Android.mk
new file mode 100644
index 0000000..b91c430
--- /dev/null
+++ b/media/native/midi/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	../../java/android/media/midi/IMidiDeviceServer.aidl \
+	midi.cpp \
+	MidiDeviceRegistry.cpp \
+	MidiPortRegistry.cpp
+
+LOCAL_AIDL_INCLUDES := \
+	$(FRAMEWORKS_BASE_JAVA_SRC_DIRS) \
+	frameworks/native/aidl/binder
+
+LOCAL_CFLAGS += -Wall -Werror -O0
+
+LOCAL_MODULE := libmidi
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := liblog libbinder libutils libmedia
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/native/midi/MidiDeviceRegistry.cpp b/media/native/midi/MidiDeviceRegistry.cpp
new file mode 100644
index 0000000..8854a08
--- /dev/null
+++ b/media/native/midi/MidiDeviceRegistry.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 "MidiDeviceRegistry.h"
+
+namespace android {
+
+ANDROID_SINGLETON_STATIC_INSTANCE(media::midi::MidiDeviceRegistry);
+
+namespace media {
+namespace midi {
+
+MidiDeviceRegistry::MidiDeviceRegistry() : mNextDeviceToken(1) {
+}
+
+status_t MidiDeviceRegistry::addDevice(sp<BpMidiDeviceServer> server, int32_t deviceId) {
+    if (server.get() == nullptr) {
+        return -EINVAL;
+    }
+
+    std::lock_guard<std::mutex> guard(mMapsLock);
+    mServers[deviceId] = server;
+    return OK;
+}
+
+status_t MidiDeviceRegistry::removeDevice(int32_t deviceId) {
+    std::lock_guard<std::mutex> guard(mMapsLock);
+    mServers.erase(deviceId);
+    const auto& iter = mUidToToken.find(deviceId);
+    if (iter != mUidToToken.end()) {
+        mTokenToUid.erase(iter->second);
+        mUidToToken.erase(iter);
+    }
+    return OK;
+}
+
+//NOTE: This creates an entry if not found, or returns an existing one.
+status_t MidiDeviceRegistry::obtainDeviceToken(int32_t deviceId, AMIDI_Device *deviceTokenPtr) {
+    std::lock_guard<std::mutex> guard(mMapsLock);
+    const auto& serversIter = mServers.find(deviceId);
+    if (serversIter == mServers.end()) {
+        // Not found.
+        return -EINVAL;
+    }
+
+    const auto& iter = mUidToToken.find(deviceId);
+    if (iter != mUidToToken.end()) {
+        *deviceTokenPtr = iter->second;
+    } else {
+        *deviceTokenPtr = mNextDeviceToken++;
+        mTokenToUid[*deviceTokenPtr] = deviceId;
+        mUidToToken[deviceId] = *deviceTokenPtr;
+    }
+    return OK;
+}
+
+status_t MidiDeviceRegistry::releaseDevice(AMIDI_Device deviceToken) {
+    std::lock_guard<std::mutex> guard(mMapsLock);
+    const auto& iter = mTokenToUid.find(deviceToken);
+    if (iter == mTokenToUid.end()) {
+        // Not found
+        return -EINVAL;
+    }
+
+    mServers.erase(iter->second);
+    mUidToToken.erase(iter->second);
+    mTokenToUid.erase(iter);
+    return OK;
+}
+
+status_t MidiDeviceRegistry::getDeviceByToken(
+        AMIDI_Device deviceToken, sp<BpMidiDeviceServer> *devicePtr) {
+    std::lock_guard<std::mutex> guard(mMapsLock);
+    int32_t id = -1;
+    {
+        const auto& iter = mTokenToUid.find(deviceToken);
+        if (iter == mTokenToUid.end()) {
+            return -EINVAL;
+        }
+        id = iter->second;
+    }
+    const auto& iter = mServers.find(id);
+    if (iter == mServers.end()) {
+        return -EINVAL;
+    }
+
+    *devicePtr = iter->second;
+    return OK;
+}
+
+} // namespace midi
+} // namespace media
+} // namespace android
diff --git a/media/native/midi/MidiDeviceRegistry.h b/media/native/midi/MidiDeviceRegistry.h
new file mode 100644
index 0000000..93be733
--- /dev/null
+++ b/media/native/midi/MidiDeviceRegistry.h
@@ -0,0 +1,104 @@
+/*
+ * 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 ANDROID_MEDIA_MIDI_DEVICE_REGISTRY_H_
+#define ANDROID_MEDIA_MIDI_DEVICE_REGISTRY_H_
+
+#include <map>
+#include <mutex>
+
+#include <binder/IBinder.h>
+#include <utils/Errors.h>
+#include <utils/Singleton.h>
+
+#include "android/media/midi/BpMidiDeviceServer.h"
+#include "midi.h"
+
+namespace android {
+namespace media {
+namespace midi {
+
+/*
+ * Maintains a thread-safe, (singleton) list of MIDI devices with associated Binder interfaces,
+ * which are exposed to the Native API via (Java) MidiDevice.mirrorToNative() &
+ * MidiDevice.removeFromNative().
+ * (Called via MidiDeviceManager::addDevice() MidiManager::removeDevice()).
+ */
+class MidiDeviceRegistry : public Singleton<MidiDeviceRegistry> {
+  public:
+    /* Add a MIDI Device to the registry.
+     *
+     * server       The Binder interface to the MIDI device server.
+     * deviceUId    The unique ID of the device obtained from
+     *              the Java API via MidiDeviceInfo.getId().
+     */
+    status_t addDevice(sp<BpMidiDeviceServer> server, int32_t deviceId);
+
+    /* Remove the device (and associated server) from the Device registry.
+     *
+     * deviceUid    The ID of the device which was used in the call to addDevice().
+     */
+    status_t removeDevice(int32_t deviceId);
+
+    /* Gets a device token associated with the device ID. This is used by the
+     * native API to identify/access the device.
+     * Multiple calls without releasing the token will return the same value.
+     *
+     * deviceUid    The ID of the device.
+     * deviceTokenPtr Receives the device (native) token associated with the device ID.
+     * returns: OK on success, error code otherwise.
+     */
+    status_t obtainDeviceToken(int32_t deviceId, AMIDI_Device *deviceTokenPtr);
+
+    /*
+     * Releases the native API device token associated with a MIDI device.
+     *
+     * deviceToken The device (native) token associated with the device ID.
+     */
+    status_t releaseDevice(AMIDI_Device deviceToken);
+
+    /*
+     * Gets the Device server binder interface associated with the device token.
+     *
+     * deviceToken The device (native) token associated with the device ID.
+     */
+    status_t getDeviceByToken(AMIDI_Device deviceToken, sp<BpMidiDeviceServer> *devicePtr);
+
+  private:
+    friend class Singleton<MidiDeviceRegistry>;
+    MidiDeviceRegistry();
+
+    // Access Mutex
+    std::mutex                              mMapsLock;
+
+    // maps device IDs to servers
+    std::map<int32_t, sp<BpMidiDeviceServer>>   mServers;
+
+    // maps device tokens to device ID
+    std::map<AMIDI_Device, int32_t>         mTokenToUid;
+
+    // maps device IDs to device tokens
+    std::map<int32_t, AMIDI_Device>         mUidToToken;
+
+    // Value of next device token to dole out.
+    AMIDI_Device                            mNextDeviceToken;
+};
+
+} // namespace midi
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_MIDI_DEVICE_REGISTRY_H_
diff --git a/media/native/midi/MidiPortRegistry.cpp b/media/native/midi/MidiPortRegistry.cpp
new file mode 100644
index 0000000..fa70af8
--- /dev/null
+++ b/media/native/midi/MidiPortRegistry.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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 "MidiPortRegistry.h"
+
+namespace android {
+
+ANDROID_SINGLETON_STATIC_INSTANCE(media::midi::MidiPortRegistry);
+
+namespace media {
+namespace midi {
+
+//TODO Note that these 2 are identical
+struct MidiPortRegistry::OutputPort {
+    AMIDI_Device device;
+    sp<IBinder> binderToken;
+    base::unique_fd ufd;
+};
+
+struct MidiPortRegistry::InputPort {
+    AMIDI_Device device;
+    sp<IBinder> binderToken;
+    base::unique_fd ufd;
+};
+
+MidiPortRegistry::MidiPortRegistry() : mNextOutputPortToken(0), mNextInputPortToken(0) {
+}
+
+status_t MidiPortRegistry::addOutputPort(
+        AMIDI_Device device,
+        sp<IBinder> portToken,
+        base::unique_fd &&ufd,
+        AMIDI_OutputPort *portPtr) {
+    *portPtr = mNextOutputPortToken++;
+
+    OutputPortEntry* portEntry = new OutputPortEntry;
+    portEntry->port = new OutputPort;
+    portEntry->state = MIDI_OUTPUT_PORT_STATE_OPEN_IDLE;
+    portEntry->port = new OutputPort;
+    portEntry->port->device = device;
+    portEntry->port->binderToken = portToken;
+    portEntry->port->ufd = std::move(ufd);
+
+    mOutputPortMap[*portPtr] = portEntry;
+
+    return OK;
+}
+
+status_t MidiPortRegistry::removeOutputPort(
+        AMIDI_OutputPort port,
+        AMIDI_Device *devicePtr,
+        sp<IBinder> *portTokenPtr) {
+    OutputPortMap::iterator itr = mOutputPortMap.find(port);
+    if (itr == mOutputPortMap.end()) {
+        return -EINVAL;
+    }
+
+    OutputPortEntry *entry = mOutputPortMap[port];
+    int portState = MIDI_OUTPUT_PORT_STATE_OPEN_IDLE;
+    while (!entry->state.compare_exchange_weak(portState, MIDI_OUTPUT_PORT_STATE_CLOSED)) {
+        if (portState == MIDI_OUTPUT_PORT_STATE_CLOSED) {
+            return -EINVAL; // Already closed
+        }
+    }
+    *devicePtr = entry->port->device;
+    *portTokenPtr = entry->port->binderToken;
+    delete entry->port;
+    entry->port = nullptr;
+
+    mOutputPortMap.erase(itr);
+
+    return OK;
+}
+
+status_t MidiPortRegistry::getOutputPortFdAndLock(
+        AMIDI_OutputPort port, base::unique_fd **ufdPtr) {
+    if (mOutputPortMap.find(port) == mOutputPortMap.end()) {
+        return -EINVAL;
+    }
+
+    OutputPortEntry *entry = mOutputPortMap[port];
+    int portState = MIDI_OUTPUT_PORT_STATE_OPEN_IDLE;
+    if (!entry->state.compare_exchange_strong(portState, MIDI_OUTPUT_PORT_STATE_OPEN_ACTIVE)) {
+        // The port has been closed.
+        return -EPIPE;
+    }
+    *ufdPtr = &entry->port->ufd;
+
+    return OK;
+}
+
+status_t MidiPortRegistry::unlockOutputPort(AMIDI_OutputPort port) {
+    if (mOutputPortMap.find(port) == mOutputPortMap.end()) {
+        return -EINVAL;
+    }
+
+    OutputPortEntry *entry = mOutputPortMap[port];
+    entry->state.store(MIDI_OUTPUT_PORT_STATE_OPEN_IDLE);
+    return OK;
+}
+
+status_t MidiPortRegistry::addInputPort(
+        AMIDI_Device device,
+        sp<IBinder> portToken,
+        base::unique_fd &&ufd,
+        AMIDI_InputPort *portPtr) {
+    *portPtr = mNextInputPortToken++;
+
+    InputPortEntry *entry = new InputPortEntry;
+
+    entry->state = MIDI_INPUT_PORT_STATE_OPEN_IDLE;
+    entry->port = new InputPort;
+    entry->port->device = device;
+    entry->port->binderToken = portToken;
+    entry->port->ufd = std::move(ufd);
+
+    mInputPortMap[*portPtr] = entry;
+
+    return OK;
+}
+
+status_t MidiPortRegistry::removeInputPort(
+        AMIDI_InputPort port,
+        AMIDI_Device *devicePtr,
+        sp<IBinder> *portTokenPtr) {
+    InputPortMap::iterator itr = mInputPortMap.find(port);
+    if (itr == mInputPortMap.end()) {
+        return -EINVAL;
+    }
+
+    InputPortEntry *entry = mInputPortMap[port];
+    int portState = MIDI_INPUT_PORT_STATE_OPEN_IDLE;
+    while (!entry->state.compare_exchange_weak(portState, MIDI_INPUT_PORT_STATE_CLOSED)) {
+        if (portState == MIDI_INPUT_PORT_STATE_CLOSED) return -EINVAL; // Already closed
+    }
+
+    *devicePtr = entry->port->device;
+    *portTokenPtr = entry->port->binderToken;
+    delete entry->port;
+    entry->port = nullptr;
+
+    mInputPortMap.erase(itr);
+
+    return OK;
+}
+
+status_t MidiPortRegistry::getInputPortFd(AMIDI_InputPort port, base::unique_fd **ufdPtr) {
+    if (mInputPortMap.find(port) == mInputPortMap.end()) {
+        return -EINVAL;
+    }
+
+    InputPortEntry *entry = mInputPortMap[port];
+
+    *ufdPtr = &entry->port->ufd;
+
+    return OK;
+}
+
+status_t MidiPortRegistry::getInputPortFdAndLock(AMIDI_InputPort port, base::unique_fd **ufdPtr) {
+    if (mInputPortMap.find(port) == mInputPortMap.end()) {
+        return -EINVAL;
+    }
+
+    InputPortEntry *entry = mInputPortMap[port];
+
+    int portState = MIDI_INPUT_PORT_STATE_OPEN_IDLE;
+    if (!entry->state.compare_exchange_strong(portState, MIDI_INPUT_PORT_STATE_OPEN_ACTIVE)) {
+        // The port has been closed.
+        return -EPIPE;
+    }
+    *ufdPtr = &entry->port->ufd;
+    return OK;
+}
+
+status_t MidiPortRegistry::MidiPortRegistry::unlockInputPort(AMIDI_InputPort port) {
+    if (mInputPortMap.find(port) == mInputPortMap.end()) {
+        return -EINVAL;
+    }
+
+    InputPortEntry *entry = mInputPortMap[port];
+    entry->state.store(MIDI_INPUT_PORT_STATE_OPEN_IDLE);
+    return OK;
+}
+
+} // namespace midi
+} // namespace media
+} // namespace android
diff --git a/media/native/midi/MidiPortRegistry.h b/media/native/midi/MidiPortRegistry.h
new file mode 100644
index 0000000..f1ffb78
--- /dev/null
+++ b/media/native/midi/MidiPortRegistry.h
@@ -0,0 +1,192 @@
+/*
+ * 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 ANDROID_MEDIA_MIDI_PORT_REGISTRY_H_
+#define ANDROID_MEDIA_MIDI_PORT_REGISTRY_H_
+
+#include <atomic>
+#include <map>
+
+#include <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <utils/Errors.h>
+#include <utils/Singleton.h>
+
+#include "midi.h"
+
+namespace android {
+namespace media {
+namespace midi {
+
+/*
+ * Maintains lists of all active input and output MIDI ports and controls access to them. Provides
+ * exclusive access to specific MIDI ports.
+ */
+class MidiPortRegistry : public Singleton<MidiPortRegistry> {
+  public:
+    /*
+     * Creates an output port entry and associates it with the specified MIDI device.
+     * Called by AMIDI_openOutputPort();
+     *
+     * device       The native API device ID.
+     * portToken    The port token (returned from the device server).
+     * udf          File descriptor for the data port associated with the MIDI output port.
+     * portPtr      Receives the native API port ID of the port being opened.
+     */
+    status_t addOutputPort(
+            AMIDI_Device device,
+            sp<IBinder> portToken,
+            base::unique_fd &&ufd,
+            AMIDI_OutputPort *portPtr);
+
+    /*
+     * Removes for the output port list a previously added output port.
+     * Called by AMIDI_closeOutputPort();
+     *
+     * port         The native API port ID of the port being closed.
+     * devicePtr    Receives the native API device ID associated with the port.
+     * portTokenPtr Receives the binder token associated with the port.
+     */
+    status_t removeOutputPort(
+            AMIDI_OutputPort port,
+            AMIDI_Device *devicePtr,
+            sp<IBinder> *portTokenPtr);
+
+    /*
+     * Creates an input port entry and associates it with the specified MIDI device.
+     * Called by AMIDI_openInputPort();
+     *
+     * device       The native API device ID.
+     * portToken    The port token (returned from the device server).
+     * udf          File descriptor for the data port associated with the MIDI input port.
+     * portPtr      Receives the native API port ID of the port being opened.
+     */
+    status_t addInputPort(
+            AMIDI_Device device,
+            sp<IBinder> portToken,
+            base::unique_fd &&ufd,
+            AMIDI_InputPort *portPtr);
+
+    /*
+     * Removes for the input port list a previously added input port.
+     * Called by AMIDI_closeINputPort();
+     *
+     * port         The native API port ID of the port being closed.
+     * devicePtr    Receives the native API device ID associated with the port.
+     * portTokenPtr Receives the binder token associated with the port.
+     */
+    status_t removeInputPort(
+            AMIDI_InputPort port,
+            AMIDI_Device *devicePtr,
+            sp<IBinder> *portTokenPtr);
+
+    /*
+     * Retrieves an exclusive-access file descriptor for an output port.
+     * Called from AMIDI_receive().
+     *
+     * port     The native API id of the output port.
+     * ufdPtr   Receives the exclusive-access file descriptor for the output port.
+     */
+    status_t getOutputPortFdAndLock(AMIDI_OutputPort port, base::unique_fd **ufdPtr);
+
+    /*
+     * Releases exclusive-access to the port and invalidates the previously received file
+     * descriptor.
+     * Called from AMIDI_receive().
+     *
+     * port The native API id of the output port.
+     */
+    status_t unlockOutputPort(AMIDI_OutputPort port);
+
+    /*
+     * Retrieves an exclusive-access file descriptor for an input port.
+     * (Not being used as (perhaps) AMIDI_sendWithTimestamp() doesn't need exclusive access
+     * to the port).
+     *
+     * port     The native API id of the input port.
+     * ufdPtr   Receives the exclusive-access file descriptor for the input port.
+     */
+    status_t getInputPortFdAndLock(AMIDI_InputPort port, base::unique_fd **ufdPtr);
+
+    /*
+     * Releases exclusive-access to the port and invalidates the previously received file
+     * descriptor.
+     * (Not used. See above).
+     *
+     * port The native API id of the input port.
+     */
+    status_t unlockInputPort(AMIDI_InputPort port);
+
+    /*
+     * Retrieves an unlocked (multi-access) file descriptor for an input port.
+     * Used by AMIDI_sendWith(), AMIDI_sendWithTimestamp & AMIDI_flush.
+     *
+     * port     The native API id of the input port.
+     * ufdPtr   Receives the multi-access file descriptor for the input port.
+     */
+    status_t getInputPortFd(AMIDI_InputPort port, base::unique_fd **ufdPtr);
+
+  private:
+    friend class Singleton<MidiPortRegistry>;
+    MidiPortRegistry();
+
+    /*
+     * Output (data receiving) ports.
+     */
+    struct OutputPort;
+    enum {
+        MIDI_OUTPUT_PORT_STATE_CLOSED = 0,
+        MIDI_OUTPUT_PORT_STATE_OPEN_IDLE,
+        MIDI_OUTPUT_PORT_STATE_OPEN_ACTIVE
+    };
+
+    struct OutputPortEntry {
+        std::atomic_int state;
+        OutputPort *port;
+    };
+
+    typedef std::map<AMIDI_OutputPort, OutputPortEntry*> OutputPortMap;
+    // Access is synchronized per record via 'state' field.
+    std::atomic<AMIDI_OutputPort> mNextOutputPortToken;
+    OutputPortMap  mOutputPortMap;
+
+    /*
+     * Input (data sending) ports.
+     */
+    struct InputPort;
+    enum {
+        MIDI_INPUT_PORT_STATE_CLOSED = 0,
+        MIDI_INPUT_PORT_STATE_OPEN_IDLE,
+        MIDI_INPUT_PORT_STATE_OPEN_ACTIVE
+    };
+
+    struct InputPortEntry {
+        std::atomic_int state;
+        InputPort *port;
+    };
+
+    typedef std::map<AMIDI_OutputPort, InputPortEntry*> InputPortMap;
+    // Access is synchronized per record via 'state' field.
+    std::atomic<AMIDI_InputPort> mNextInputPortToken;
+    InputPortMap  mInputPortMap;
+
+};
+
+} // namespace midi
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_MIDI_PORT_REGISTRY_H_
diff --git a/media/native/midi/midi.cpp b/media/native/midi/midi.cpp
new file mode 100644
index 0000000..1bf0bd0
--- /dev/null
+++ b/media/native/midi/midi.cpp
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "NativeMIDI"
+
+#include <poll.h>
+#include <unistd.h>
+
+#include <binder/Binder.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include "android/media/midi/BpMidiDeviceServer.h"
+#include "media/MidiDeviceInfo.h"
+#include "MidiDeviceRegistry.h"
+#include "MidiPortRegistry.h"
+
+#include "midi.h"
+
+using android::IBinder;
+using android::BBinder;
+using android::OK;
+using android::sp;
+using android::status_t;
+using android::base::unique_fd;
+using android::binder::Status;
+using android::media::midi::BpMidiDeviceServer;
+using android::media::midi::MidiDeviceInfo;
+using android::media::midi::MidiDeviceRegistry;
+using android::media::midi::MidiPortRegistry;
+
+#define SIZE_MIDIRECEIVEBUFFER AMIDI_BUFFER_SIZE
+
+/* TRANSFER PACKET FORMAT (as defined in MidiPortImpl.java)
+ *
+ * Transfer packet format is as follows (see MidiOutputPort.mThread.run() to see decomposition):
+ * |oc|md|md| ......... |md|ts|ts|ts|ts|ts|ts|ts|ts|
+ *  ^ +--------------------+-----------------------+
+ *  |  ^                    ^
+ *  |  |                    |
+ *  |  |                    + timestamp (8 bytes)
+ *  |  |
+ *  |  + MIDI data bytes (numBytes bytes)
+ *  |
+ *  + OpCode (AMIDI_OPCODE_DATA)
+ *
+ *  NOTE: The socket pair is configured to use SOCK_SEQPACKET mode.
+ *  SOCK_SEQPACKET, for a sequenced-packet socket that is connection-oriented, preserves message
+ *  boundaries, and delivers messages in the order that they were sent.
+ *  So 'read()' always returns a whole message.
+ */
+
+status_t AMIDI_getDeviceById(int32_t id, AMIDI_Device *devicePtr) {
+    return MidiDeviceRegistry::getInstance().obtainDeviceToken(id, devicePtr);
+}
+
+status_t AMIDI_getDeviceInfo(AMIDI_Device device, AMIDI_DeviceInfo *deviceInfoPtr) {
+    sp<BpMidiDeviceServer> deviceServer;
+    status_t result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
+    if (result != OK) {
+        ALOGE("AMIDI_getDeviceInfo bad device token %d: %d", device, result);
+        return result;
+    }
+
+    MidiDeviceInfo deviceInfo;
+    Status txResult = deviceServer->getDeviceInfo(&deviceInfo);
+    if (!txResult.isOk()) {
+        ALOGE("AMIDI_getDeviceInfo transaction error: %d", txResult.transactionError());
+        return txResult.transactionError();
+    }
+
+    deviceInfoPtr->type = deviceInfo.getType();
+    deviceInfoPtr->uid = deviceInfo.getUid();
+    deviceInfoPtr->isPrivate = deviceInfo.isPrivate();
+    deviceInfoPtr->inputPortCount = deviceInfo.getInputPortNames().size();
+    deviceInfoPtr->outputPortCount = deviceInfo.getOutputPortNames().size();
+    return OK;
+}
+
+/*
+ * Output (receiving) API
+ */
+status_t AMIDI_openOutputPort(AMIDI_Device device, int portNumber, AMIDI_OutputPort *outputPortPtr) {
+    sp<BpMidiDeviceServer> deviceServer;
+    status_t result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
+    if (result != OK) {
+        ALOGE("AMIDI_openOutputPort bad device token %d: %d", device, result);
+        return result;
+    }
+
+    sp<BBinder> portToken(new BBinder());
+    unique_fd ufd;
+    Status txResult = deviceServer->openOutputPort(portToken, portNumber, &ufd);
+    if (!txResult.isOk()) {
+        ALOGE("AMIDI_openOutputPort transaction error: %d", txResult.transactionError());
+        return txResult.transactionError();
+    }
+
+    result = MidiPortRegistry::getInstance().addOutputPort(
+            device, portToken, std::move(ufd), outputPortPtr);
+    if (result != OK) {
+        ALOGE("AMIDI_openOutputPort port registration error: %d", result);
+        // Close port
+        return result;
+    }
+    return OK;
+}
+
+ssize_t AMIDI_receive(AMIDI_OutputPort outputPort, AMIDI_Message *messages, ssize_t maxMessages) {
+    unique_fd *ufd;
+    // TODO: May return a nicer self-unlocking object
+    status_t result = MidiPortRegistry::getInstance().getOutputPortFdAndLock(outputPort, &ufd);
+    if (result != OK) {
+        return result;
+    }
+
+    ssize_t messagesRead = 0;
+    while (messagesRead < maxMessages) {
+        struct pollfd checkFds[1] = { { *ufd, POLLIN, 0 } };
+        int pollResult = poll(checkFds, 1, 0);
+        if (pollResult < 1) {
+            result = android::INVALID_OPERATION;
+            break;
+        }
+
+        AMIDI_Message *message = &messages[messagesRead];
+        uint8_t readBuffer[AMIDI_PACKET_SIZE];
+        memset(readBuffer, 0, sizeof(readBuffer));
+        ssize_t readCount = read(*ufd, readBuffer, sizeof(readBuffer));
+        if (readCount == EINTR) {
+            continue;
+        }
+        if (readCount < 1) {
+            result = android::NOT_ENOUGH_DATA;
+            break;
+        }
+
+        // set Packet Format definition at the top of this file.
+        size_t dataSize = 0;
+        message->opcode = readBuffer[0];
+        message->timestamp = 0;
+        if (message->opcode == AMIDI_OPCODE_DATA && readCount >= AMIDI_PACKET_OVERHEAD) {
+            dataSize = readCount - AMIDI_PACKET_OVERHEAD;
+            if (dataSize) {
+                memcpy(message->buffer, readBuffer + 1, dataSize);
+            }
+            message->timestamp = *(uint64_t*) (readBuffer + readCount - sizeof(uint64_t));
+        }
+        message->len = dataSize;
+        ++messagesRead;
+    }
+
+    MidiPortRegistry::getInstance().unlockOutputPort(outputPort);
+    return result == OK ? messagesRead : result;
+}
+
+status_t AMIDI_closeOutputPort(AMIDI_OutputPort outputPort) {
+    AMIDI_Device device;
+    sp<IBinder> portToken;
+    status_t result =
+        MidiPortRegistry::getInstance().removeOutputPort(outputPort, &device, &portToken);
+    if (result != OK) {
+        return result;
+    }
+
+    sp<BpMidiDeviceServer> deviceServer;
+    result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
+    if (result != OK) {
+        return result;
+    }
+
+    Status txResult = deviceServer->closePort(portToken);
+    if (!txResult.isOk()) {
+        return txResult.transactionError();
+    }
+    return OK;
+}
+
+/*
+ * Input (sending) API
+ */
+status_t AMIDI_openInputPort(AMIDI_Device device, int portNumber, AMIDI_InputPort *inputPortPtr) {
+    sp<BpMidiDeviceServer> deviceServer;
+    status_t result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
+    if (result != OK) {
+        ALOGE("AMIDI_openInputPort bad device token %d: %d", device, result);
+        return result;
+    }
+
+    sp<BBinder> portToken(new BBinder());
+    unique_fd ufd; // this is the file descriptor of the "receive" port s
+    Status txResult = deviceServer->openInputPort(portToken, portNumber, &ufd);
+    if (!txResult.isOk()) {
+        ALOGE("AMIDI_openInputPort transaction error: %d", txResult.transactionError());
+        return txResult.transactionError();
+    }
+
+    result = MidiPortRegistry::getInstance().addInputPort(
+            device, portToken, std::move(ufd), inputPortPtr);
+    if (result != OK) {
+        ALOGE("AMIDI_openInputPort port registration error: %d", result);
+        // Close port
+        return result;
+    }
+
+    return OK;
+}
+
+status_t AMIDI_closeInputPort(AMIDI_InputPort inputPort) {
+    AMIDI_Device device;
+    sp<IBinder> portToken;
+    status_t result = MidiPortRegistry::getInstance().removeInputPort(
+            inputPort, &device, &portToken);
+    if (result != OK) {
+        ALOGE("AMIDI_closeInputPort remove port error: %d", result);
+        return result;
+    }
+
+    sp<BpMidiDeviceServer> deviceServer;
+    result = MidiDeviceRegistry::getInstance().getDeviceByToken(device, &deviceServer);
+    if (result != OK) {
+        ALOGE("AMIDI_closeInputPort can't find device error: %d", result);
+        return result;
+    }
+
+    Status txResult = deviceServer->closePort(portToken);
+    if (!txResult.isOk()) {
+        result = txResult.transactionError();
+        ALOGE("AMIDI_closeInputPort transaction error: %d", result);
+        return result;
+    }
+
+    return OK;
+}
+
+ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort /*inputPort*/) {
+    return SIZE_MIDIRECEIVEBUFFER;
+}
+
+static ssize_t AMIDI_makeSendBuffer(uint8_t *buffer, uint8_t *data, ssize_t numBytes, uint64_t timestamp) {
+    buffer[0] = AMIDI_OPCODE_DATA;
+    memcpy(buffer + 1, data, numBytes);
+    memcpy(buffer + 1 + numBytes, &timestamp, sizeof(timestamp));
+    return numBytes + AMIDI_PACKET_OVERHEAD;
+}
+
+// Handy debugging function.
+//static void AMIDI_logBuffer(uint8_t *data, size_t numBytes) {
+//    for (size_t index = 0; index < numBytes; index++) {
+//      ALOGI("  data @%zu [0x%X]", index, data[index]);
+//    }
+//}
+
+ssize_t AMIDI_send(AMIDI_InputPort inputPort, uint8_t *buffer, ssize_t numBytes) {
+    return AMIDI_sendWithTimestamp(inputPort, buffer, numBytes, 0);
+}
+
+ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort inputPort, uint8_t *data,
+        ssize_t numBytes, int64_t timestamp) {
+
+    if (numBytes > SIZE_MIDIRECEIVEBUFFER) {
+        return android::BAD_VALUE;
+    }
+
+    // AMIDI_logBuffer(data, numBytes);
+
+    unique_fd *ufd = NULL;
+    status_t result = MidiPortRegistry::getInstance().getInputPortFd(inputPort, &ufd);
+    if (result != OK) {
+        return result;
+    }
+
+    uint8_t writeBuffer[SIZE_MIDIRECEIVEBUFFER + AMIDI_PACKET_OVERHEAD];
+    ssize_t numTransferBytes = AMIDI_makeSendBuffer(writeBuffer, data, numBytes, timestamp);
+    ssize_t numWritten = write(*ufd, writeBuffer, numTransferBytes);
+
+    if (numWritten < numTransferBytes) {
+        ALOGE("AMIDI_sendWithTimestamp Couldn't write MIDI data buffer. requested:%zu, written%zu",
+                numTransferBytes, numWritten);
+    }
+
+    return numWritten - AMIDI_PACKET_OVERHEAD;
+}
+
+status_t AMIDI_flush(AMIDI_InputPort inputPort) {
+    unique_fd *ufd = NULL;
+    status_t result = MidiPortRegistry::getInstance().getInputPortFd(inputPort, &ufd);
+    if (result != OK) {
+        return result;
+    }
+
+    uint8_t opCode = AMIDI_OPCODE_FLUSH;
+    ssize_t numTransferBytes = 1;
+    ssize_t numWritten = write(*ufd, &opCode, numTransferBytes);
+
+    if (numWritten < numTransferBytes) {
+        ALOGE("AMIDI_flush Couldn't write MIDI flush. requested:%zu, written%zu",
+                numTransferBytes, numWritten);
+        return android::INVALID_OPERATION;
+    }
+
+    return OK;
+}
+
diff --git a/media/native/midi/midi.h b/media/native/midi/midi.h
new file mode 100644
index 0000000..717bc66
--- /dev/null
+++ b/media/native/midi/midi.h
@@ -0,0 +1,202 @@
+/*
+ * 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 ANDROID_MEDIA_MIDI_H_
+#define ANDROID_MEDIA_MIDI_H_
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+
+using android::status_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//typedef struct _AMIDI_Device;
+//typedef struct _AMIDI_InputPort;
+//typedef struct _AMIDI_OutputPort;
+//typedef _AMIDI_Device*      AMIDI_Device;
+//typedef _AMIDI_InputPort*   AMIDI_InputPort;
+//typedef _AMIDI_OutputPort*  AMIDI_OutputPort;
+
+typedef int32_t AMIDI_Device;
+typedef int32_t AMIDI_InputPort;
+typedef int32_t  AMIDI_OutputPort;
+
+//TODO - Do we want to wrap this stuff in namespace android { namespace media { namespace midi {?
+
+enum {
+    AMIDI_INVALID_HANDLE = -1
+};
+
+enum {
+    AMIDI_OPCODE_DATA = 1,
+    AMIDI_OPCODE_FLUSH = 2,
+    AMIDI_PACKET_SIZE = 1024,  /* !!! Currently MidiPortImpl.MAX_PACKET_SIZE !!! */
+    AMIDI_PACKET_OVERHEAD = 9,
+    AMIDI_BUFFER_SIZE = AMIDI_PACKET_SIZE - AMIDI_PACKET_OVERHEAD
+            /* !!! TBD, currently MidiPortImpl.MAX_PACKET_DATA_SIZE !!! */
+};
+
+typedef struct {
+    uint32_t opcode;
+    uint8_t buffer[AMIDI_BUFFER_SIZE];
+    size_t len;
+    int64_t timestamp;
+} AMIDI_Message;
+
+enum {
+    AMIDI_DEVICE_TYPE_USB = 1,
+    AMIDI_DEVICE_TYPE_VIRTUAL = 2,
+    AMIDI_DEVICE_TYPE_BLUETOOTH = 3
+};
+
+typedef struct {
+    int32_t type;
+    int32_t uid;
+    int32_t isPrivate;
+    int32_t inputPortCount;
+    int32_t outputPortCount;
+} AMIDI_DeviceInfo;
+
+/*
+ * Device API
+ */
+/*
+ * Retrieves the native API device token for the specified Java API device ID.
+ *
+ * uid          The Java API id of the device.
+ * devicePtr    Receives the associated native API token for the device.
+ *
+ * Returns OK or a (negative) error code.
+ */
+status_t AMIDI_getDeviceById(int32_t id, AMIDI_Device *devicePtr);
+
+/*
+ * Retrieves information for the native MIDI device.
+ *
+ * device           The Native API token for the device.
+ * deviceInfoPtr    Receives the associated device info.
+ *
+ * Returns OK or a (negative) error code.
+ */
+status_t AMIDI_getDeviceInfo(AMIDI_Device device, AMIDI_DeviceInfo *deviceInfoPtr);
+
+/*
+ * API for receiving data from the Output port of a device.
+ */
+/*
+ * Opens the output port.
+ *
+ * device           Identifies the device.
+ * portNumber       Specifies the zero-based port index on the device to open.
+ * outputPortPtr    Receives the native API port identifier of the opened port.
+ *
+ * Returns OK, or a (negative) error code.
+ */
+status_t AMIDI_openOutputPort(AMIDI_Device device, int portNumber, AMIDI_OutputPort *outputPortPtr);
+
+/*
+ * Receives any pending MIDI messages (up to the specified maximum number of messages).
+ *
+ * outputPort   Identifies the port to receive messages from.
+ * messages     Points to an array (size maxMessages) to receive the MIDI messages.
+ * maxMessages  The number of messages allocated in the messages array.
+ *
+ * Returns the number of messages received, or a (negative) error code.
+ */
+ssize_t AMIDI_receive(AMIDI_OutputPort outputPort, AMIDI_Message *messages, ssize_t maxMessages);
+
+/*
+ * Closes the output port.
+ *
+ * outputPort   The native API port identifier of the port.
+ *
+ * Returns OK, or a (negative) error code.
+ */
+status_t AMIDI_closeOutputPort(AMIDI_OutputPort outputPort);
+
+/*
+ * API for sending data to the Input port of a device.
+ */
+/*
+ * Opens the input port.
+ *
+ * device           Identifies the device.
+ * portNumber       Specifies the zero-based port index on the device to open.
+ * inputPortPtr     Receives the native API port identifier of the opened port.
+ *
+ * Returns OK, or a (negative) error code.
+ */
+status_t AMIDI_openInputPort(AMIDI_Device device, int portNumber, AMIDI_InputPort *inputPortPtr);
+
+/*
+ * Returns the maximum number of bytes that can be received in a single MIDI message.
+ */
+ssize_t AMIDI_getMaxMessageSizeInBytes(AMIDI_InputPort inputPort);
+
+/*
+ * Sends data to the specified input port.
+ *
+ * inputPort    The native API identifier of the port to send data to.
+ * buffer       Points to the array of bytes containing the data to send.
+ * numBytes     Specifies the number of bytes to write.
+ *
+ * Returns  The number of bytes sent or a (negative) error code.
+ */
+ssize_t AMIDI_send(AMIDI_InputPort inputPort, uint8_t *buffer, ssize_t numBytes);
+
+/*
+ * Sends data to the specified input port with a timestamp.
+ *
+ * inputPort    The native API identifier of the port to send data to.
+ * buffer       Points to the array of bytes containing the data to send.
+ * numBytes     Specifies the number of bytes to write.
+ * timestamp    The time stamp to associate with the sent data.
+ *
+ * Returns  The number of bytes sent or a (negative) error code.
+ */
+ssize_t AMIDI_sendWithTimestamp(AMIDI_InputPort inputPort, uint8_t *buffer,
+        ssize_t numBytes, int64_t timestamp);
+
+/*
+ * Sends a message with a 'MIDI flush command code' to the specified port.
+ *
+ * inputPort    The native API identifier of the port to send the flush message to.
+ *
+ * Returns OK, or a (negative) error code.
+ */
+status_t AMIDI_flush(AMIDI_InputPort inputPort);
+
+/*
+ * Closes the input port.
+ *
+ * inputPort   The native API port identifier of the port.
+ *
+ *
+ * Returns OK, or a (negative) error code.
+ */
+status_t AMIDI_closeInputPort(AMIDI_InputPort inputPort);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ANDROID_MEDIA_MIDI_H_ */
diff --git a/media/tests/NativeMidiDemo/Android.mk b/media/tests/NativeMidiDemo/Android.mk
new file mode 100644
index 0000000..6b08f6b
--- /dev/null
+++ b/media/tests/NativeMidiDemo/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := NativeMidiDemo
+
+#LOCAL_SDK_VERSION := current
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
+
+LOCAL_JNI_SHARED_LIBRARIES := libnativemidi_jni
+
+include $(BUILD_PACKAGE)
+
+# Include packages in subdirectories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/tests/NativeMidiDemo/AndroidManifest.xml b/media/tests/NativeMidiDemo/AndroidManifest.xml
new file mode 100644
index 0000000..322873f
--- /dev/null
+++ b/media/tests/NativeMidiDemo/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.example.android.nativemididemo"
+          android:versionCode="1"
+          android:versionName="1.0">
+  <application
+      android:allowBackup="false"
+      android:fullBackupContent="false"
+      android:icon="@mipmap/ic_launcher"
+      android:label="@string/app_name">
+    <uses-feature android:name="android.software.midi" android:required="true"/>
+    <activity android:name=".NativeMidi"
+              android:label="@string/app_name">
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.LAUNCHER" />
+      </intent-filter>
+    </activity>
+  </application>
+</manifest>
diff --git a/media/tests/NativeMidiDemo/java/com/example/android/nativemididemo/NativeMidi.java b/media/tests/NativeMidiDemo/java/com/example/android/nativemididemo/NativeMidi.java
new file mode 100644
index 0000000..0969b10
--- /dev/null
+++ b/media/tests/NativeMidiDemo/java/com/example/android/nativemididemo/NativeMidi.java
@@ -0,0 +1,354 @@
+/*
+ * 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.
+ *
+ */
+
+package com.example.android.nativemididemo;
+
+import android.app.Activity;
+import android.content.Context;
+import android.media.midi.MidiDevice;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiOutputPort;
+import android.media.midi.MidiReceiver;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+
+import java.io.IOException;
+
+public class NativeMidi extends Activity
+{
+    private TextView mCallbackStatusTextView;
+    private TextView mJavaMidiStatusTextView;
+    private TextView mMessagesTextView;
+    private RadioGroup mMidiDevicesRadioGroup;
+    private Handler mTimerHandler = new Handler();
+    private boolean mAudioWorks;
+    private final int mMinFramesPerBuffer = 32;   // See {min|max}PlaySamples in nativemidi-jni.cpp
+    private final int mMaxFramesPerBuffer = 1000;
+    private int mFramesPerBuffer;
+
+    private TouchableScrollView mMessagesContainer;
+    private MidiManager mMidiManager;
+    private MidiOutputPortSelector mActivePortSelector;
+
+    private Runnable mTimerRunnable = new Runnable() {
+        private long mLastTime;
+        private long mLastPlaybackCounter;
+        private int mLastCallbackRate;
+        private long mLastUntouchedTime;
+
+        @Override
+        public void run() {
+            final long checkIntervalMs = 1000;
+            long currentTime = System.currentTimeMillis();
+            long currentPlaybackCounter = getPlaybackCounter();
+            if (currentTime - mLastTime >= checkIntervalMs) {
+                int callbackRate = Math.round(
+                        (float)(currentPlaybackCounter - mLastPlaybackCounter) /
+                        ((float)(currentTime - mLastTime) / (float)1000));
+                if (mLastCallbackRate != callbackRate) {
+                    mCallbackStatusTextView.setText(
+                           "CB: " + callbackRate + " Hz");
+                    mLastCallbackRate = callbackRate;
+                }
+                mLastTime = currentTime;
+                mLastPlaybackCounter = currentPlaybackCounter;
+            }
+
+            String[] newMessages = getRecentMessages();
+            if (newMessages != null) {
+                for (String message : newMessages) {
+                    mMessagesTextView.append(message);
+                    mMessagesTextView.append("\n");
+                }
+                if (!mMessagesContainer.isTouched) {
+                    if (mLastUntouchedTime == 0) mLastUntouchedTime = currentTime;
+                    if (currentTime - mLastUntouchedTime > 3000) {
+                        mMessagesContainer.fullScroll(View.FOCUS_DOWN);
+                    }
+                } else {
+                    mLastUntouchedTime = 0;
+                }
+            }
+
+            mTimerHandler.postDelayed(this, checkIntervalMs / 4);
+        }
+    };
+
+    private void addMessage(final String message) {
+        mTimerHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                mMessagesTextView.append(message);
+            }
+        }, 0);
+    }
+
+    private class MidiOutputPortSelector implements View.OnClickListener {
+        private final MidiDeviceInfo mDeviceInfo;
+        private final int mPortNumber;
+        private MidiDevice mDevice;
+        private MidiOutputPort mOutputPort;
+
+        MidiOutputPortSelector() {
+            mDeviceInfo = null;
+            mPortNumber = -1;
+        }
+
+        MidiOutputPortSelector(MidiDeviceInfo info, int portNumber) {
+            mDeviceInfo = info;
+            mPortNumber = portNumber;
+        }
+
+        MidiDeviceInfo getDeviceInfo() { return mDeviceInfo; }
+
+        @Override
+        public void onClick(View v) {
+            if (mActivePortSelector != null) {
+                mActivePortSelector.close();
+                mActivePortSelector = null;
+            }
+            if (mDeviceInfo == null) {
+                mActivePortSelector = this;
+                return;
+            }
+            mMidiManager.openDevice(mDeviceInfo, new MidiManager.OnDeviceOpenedListener() {
+                @Override
+                public void onDeviceOpened(MidiDevice device) {
+                    if (device == null) {
+                        addMessage("! Failed to open MIDI device !\n");
+                    } else {
+                        mDevice = device;
+                        try {
+                            mDevice.mirrorToNative();
+                            startReadingMidi(mDevice.getInfo().getId(), mPortNumber);
+                        } catch (IOException e) {
+                            addMessage("! Failed to mirror to native !\n" + e.getMessage() + "\n");
+                        }
+
+                        mActivePortSelector = MidiOutputPortSelector.this;
+
+                        mOutputPort = device.openOutputPort(mPortNumber);
+                        mOutputPort.connect(mMidiReceiver);
+                    }
+                }
+            }, null);
+        }
+
+        void closePortOnly() {
+            stopReadingMidi();
+        }
+
+        void close() {
+            closePortOnly();
+            try {
+                if (mOutputPort != null) {
+                    mOutputPort.close();
+                }
+            } catch (IOException e) {
+                mMessagesTextView.append("! Port close error: " + e + "\n");
+            } finally {
+                mOutputPort = null;
+            }
+            try {
+                if (mDevice != null) {
+                    mDevice.close();
+                }
+            } catch (IOException e) {
+                mMessagesTextView.append("! Device close error: " + e + "\n");
+            } finally {
+                mDevice = null;
+            }
+        }
+    }
+
+    private MidiManager.DeviceCallback mMidiDeviceCallback = new MidiManager.DeviceCallback() {
+        @Override
+        public void onDeviceAdded(MidiDeviceInfo info) {
+            Bundle deviceProps = info.getProperties();
+            String deviceName = deviceProps.getString(MidiDeviceInfo.PROPERTY_NAME);
+            if (deviceName == null) {
+                deviceName = deviceProps.getString(MidiDeviceInfo.PROPERTY_MANUFACTURER);
+            }
+
+            for (MidiDeviceInfo.PortInfo port : info.getPorts()) {
+                if (port.getType() != MidiDeviceInfo.PortInfo.TYPE_OUTPUT) continue;
+                String portName = port.getName();
+                int portNumber = port.getPortNumber();
+                if (portName.length() == 0) portName = "[" + portNumber + "]";
+                portName += "@" + deviceName;
+                RadioButton outputDevice = new RadioButton(NativeMidi.this);
+                outputDevice.setText(portName);
+                outputDevice.setTag(info);
+                outputDevice.setOnClickListener(new MidiOutputPortSelector(info, portNumber));
+                mMidiDevicesRadioGroup.addView(outputDevice);
+            }
+
+            NativeMidi.this.updateKeepScreenOn();
+        }
+
+        @Override
+        public void onDeviceRemoved(MidiDeviceInfo info) {
+            if (mActivePortSelector != null && info.equals(mActivePortSelector.getDeviceInfo())) {
+                mActivePortSelector.close();
+                mActivePortSelector = null;
+            }
+            int removeButtonStart = -1, removeButtonCount = 0;
+            final int buttonCount = mMidiDevicesRadioGroup.getChildCount();
+            boolean checked = false;
+            for (int i = 0; i < buttonCount; ++i) {
+                RadioButton button = (RadioButton) mMidiDevicesRadioGroup.getChildAt(i);
+                if (!info.equals(button.getTag())) continue;
+                if (removeButtonStart == -1) removeButtonStart = i;
+                ++removeButtonCount;
+                if (button.isChecked()) checked = true;
+            }
+            if (removeButtonStart != -1) {
+                mMidiDevicesRadioGroup.removeViews(removeButtonStart, removeButtonCount);
+                if (checked) {
+                    mMidiDevicesRadioGroup.check(R.id.device_none);
+                }
+            }
+
+            NativeMidi.this.updateKeepScreenOn();
+        }
+    };
+
+    private class JavaMidiReceiver extends MidiReceiver implements Runnable {
+        @Override
+        public void onSend(byte[] data, int offset,
+                int count, long timestamp) throws IOException {
+            mTimerHandler.removeCallbacks(this);
+            mTimerHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    mJavaMidiStatusTextView.setText("Java: MSG");
+                }
+            }, 0);
+            mTimerHandler.postDelayed(this, 100);
+        }
+
+        @Override
+        public void run() {
+            mJavaMidiStatusTextView.setText("Java: ---");
+        }
+    }
+
+    private JavaMidiReceiver mMidiReceiver = new JavaMidiReceiver();
+
+    private void updateKeepScreenOn() {
+        if (mMidiDevicesRadioGroup.getChildCount() > 1) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        } else {
+            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        mCallbackStatusTextView = (TextView) findViewById(R.id.callback_status);
+        mJavaMidiStatusTextView = (TextView) findViewById(R.id.java_midi_status);
+        mMessagesTextView = (TextView) findViewById(R.id.messages);
+        mMessagesContainer = (TouchableScrollView) findViewById(R.id.messages_scroll);
+        mMidiDevicesRadioGroup = (RadioGroup) findViewById(R.id.devices);
+        RadioButton deviceNone = (RadioButton) findViewById(R.id.device_none);
+        deviceNone.setOnClickListener(new MidiOutputPortSelector());
+
+        AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+        String sampleRate = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
+        if (sampleRate == null) sampleRate = "48000";
+        String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
+        if (framesPerBuffer == null) framesPerBuffer = Integer.toString(mMaxFramesPerBuffer);
+        mFramesPerBuffer = Integer.parseInt(framesPerBuffer);
+        String audioInitResult = initAudio(Integer.parseInt(sampleRate), mFramesPerBuffer);
+        mMessagesTextView.append("Open SL ES init: " + audioInitResult + "\n");
+
+        if (audioInitResult.startsWith("Success")) {
+            mAudioWorks = true;
+            mTimerHandler.postDelayed(mTimerRunnable, 0);
+            mTimerHandler.postDelayed(mMidiReceiver, 0);
+        }
+
+        mMidiManager = (MidiManager) getSystemService(Context.MIDI_SERVICE);
+        mMidiManager.registerDeviceCallback(mMidiDeviceCallback, new Handler());
+        for (MidiDeviceInfo info : mMidiManager.getDevices()) {
+            mMidiDeviceCallback.onDeviceAdded(info);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mAudioWorks) {
+            mTimerHandler.removeCallbacks(mTimerRunnable);
+            pauseAudio();
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mAudioWorks) {
+            mTimerHandler.postDelayed(mTimerRunnable, 0);
+            resumeAudio();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (mActivePortSelector != null) {
+            mActivePortSelector.close();
+            mActivePortSelector = null;
+        }
+        shutdownAudio();
+        super.onDestroy();
+    }
+
+    public void onClearMessages(View v) {
+        mMessagesTextView.setText("");
+    }
+
+    public void onClosePort(View v) {
+        if (mActivePortSelector != null) {
+            mActivePortSelector.closePortOnly();
+        }
+    }
+
+    private native String initAudio(int sampleRate, int playSamples);
+    private native void pauseAudio();
+    private native void resumeAudio();
+    private native void shutdownAudio();
+
+    private native long getPlaybackCounter();
+    private native String[] getRecentMessages();
+
+    private native void startReadingMidi(int deviceId, int portNumber);
+    private native void stopReadingMidi();
+
+    static {
+        System.loadLibrary("nativemidi_jni");
+    }
+}
diff --git a/media/tests/NativeMidiDemo/java/com/example/android/nativemididemo/TouchableScrollView.java b/media/tests/NativeMidiDemo/java/com/example/android/nativemididemo/TouchableScrollView.java
new file mode 100644
index 0000000..645aafa
--- /dev/null
+++ b/media/tests/NativeMidiDemo/java/com/example/android/nativemididemo/TouchableScrollView.java
@@ -0,0 +1,32 @@
+package com.example.android.nativemididemo;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.util.AttributeSet;
+import android.widget.ScrollView;
+
+public class TouchableScrollView extends ScrollView {
+    public boolean isTouched;
+
+    public TouchableScrollView(Context context) {
+        super(context);
+    }
+
+    public TouchableScrollView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                isTouched = true;
+                break;
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                isTouched = false;
+                break;
+        }
+        return super.onTouchEvent(event);
+    }
+}
diff --git a/media/tests/NativeMidiDemo/jni/Android.mk b/media/tests/NativeMidiDemo/jni/Android.mk
new file mode 100644
index 0000000..69a64bd
--- /dev/null
+++ b/media/tests/NativeMidiDemo/jni/Android.mk
@@ -0,0 +1,35 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MODULE := libnativemidi_jni
+
+LOCAL_SRC_FILES := \
+	nativemidi-jni.cpp \
+	messagequeue.cpp
+
+LOCAL_CFLAGS += -Wall -Wextra -Werror -O0
+
+LOCAL_C_INCLUDES += \
+    frameworks/base/media/native
+
+LOCAL_CXX_STL := libc++_static
+
+LOCAL_SHARED_LIBRARIES := libOpenSLES libmidi
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/tests/NativeMidiDemo/jni/messagequeue.cpp b/media/tests/NativeMidiDemo/jni/messagequeue.cpp
new file mode 100644
index 0000000..ffaef38
--- /dev/null
+++ b/media/tests/NativeMidiDemo/jni/messagequeue.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ *
+ */
+
+#include <atomic>
+#include <stdio.h>
+#include <string.h>
+
+#include "messagequeue.h"
+
+namespace nativemididemo {
+
+static const int messageBufferSize = 64 * 1024;
+static char messageBuffer[messageBufferSize];
+static std::atomic_ullong messagesLastWritePosition;
+
+void writeMessage(const char* message)
+{
+    static unsigned long long lastWritePos = 0;
+    size_t messageLen = strlen(message);
+    if (messageLen == 0) return;
+
+    messageLen += 1; // Also count in the null terminator.
+    char buffer[1024];
+    if (messageLen >= messageBufferSize) {
+        snprintf(buffer, sizeof(buffer), "!!! Message too long: %zu bytes !!!", messageLen);
+        message = buffer;
+        messageLen = strlen(message);
+    }
+
+    size_t wrappedWritePos = lastWritePos % messageBufferSize;
+    if (wrappedWritePos + messageLen >= messageBufferSize) {
+        size_t tailLen = messageBufferSize - wrappedWritePos;
+        memset(messageBuffer + wrappedWritePos, 0, tailLen);
+        lastWritePos += tailLen;
+        wrappedWritePos = 0;
+    }
+
+    memcpy(messageBuffer + wrappedWritePos, message, messageLen);
+    lastWritePos += messageLen;
+    messagesLastWritePosition.store(lastWritePos);
+}
+
+static char messageBufferCopy[messageBufferSize];
+
+jobjectArray getRecentMessagesForJava(JNIEnv* env, jobject)
+{
+    static unsigned long long lastReadPos = 0;
+    const char* overrunMessage = "";
+    size_t messagesCount = 0;
+    jobjectArray result = NULL;
+
+    // First we copy the portion of the message buffer into messageBufferCopy.  If after finishing
+    // the copy we notice that the writer has mutated the portion of the buffer that we were
+    // copying, we report an overrun. Afterwards we can safely read messages from the copy.
+    memset(messageBufferCopy, 0, sizeof(messageBufferCopy));
+    unsigned long long lastWritePos = messagesLastWritePosition.load();
+    if (lastWritePos - lastReadPos > messageBufferSize) {
+        overrunMessage = "!!! Message buffer overrun !!!";
+        messagesCount = 1;
+        lastReadPos = lastWritePos;
+        goto create_array;
+    }
+    if (lastWritePos == lastReadPos) return result;
+    if (lastWritePos / messageBufferSize == lastReadPos / messageBufferSize) {
+        size_t wrappedReadPos = lastReadPos % messageBufferSize;
+        memcpy(messageBufferCopy + wrappedReadPos,
+                messageBuffer + wrappedReadPos,
+                lastWritePos % messageBufferSize - wrappedReadPos);
+    } else {
+        size_t wrappedReadPos = lastReadPos % messageBufferSize;
+        memcpy(messageBufferCopy, messageBuffer, lastWritePos % messageBufferSize);
+        memcpy(messageBufferCopy + wrappedReadPos,
+                messageBuffer + wrappedReadPos,
+                messageBufferSize - wrappedReadPos);
+    }
+    {
+    unsigned long long newLastWritePos = messagesLastWritePosition.load();
+    if (newLastWritePos - lastReadPos > messageBufferSize) {
+        overrunMessage = "!!! Message buffer overrun !!!";
+        messagesCount = 1;
+        lastReadPos = lastWritePos = newLastWritePos;
+        goto create_array;
+    }
+    }
+    // Otherwise we ignore newLastWritePos, since we only have a copy of the buffer
+    // up to lastWritePos.
+
+    for (unsigned long long readPos = lastReadPos; readPos < lastWritePos; ) {
+        size_t messageLen = strlen(messageBufferCopy + (readPos % messageBufferSize));
+        if (messageLen != 0) {
+            readPos += messageLen + 1;
+            messagesCount++;
+        } else {
+            // Skip to the beginning of the buffer.
+            readPos = (readPos / messageBufferSize + 1) * messageBufferSize;
+        }
+    }
+    if (messagesCount == 0) {
+        lastReadPos = lastWritePos;
+        return result;
+    }
+
+create_array:
+    result = env->NewObjectArray(
+            messagesCount, env->FindClass("java/lang/String"), env->NewStringUTF(overrunMessage));
+    if (lastWritePos == lastReadPos) return result;
+
+    jsize arrayIndex = 0;
+    while (lastReadPos < lastWritePos) {
+        size_t wrappedReadPos = lastReadPos % messageBufferSize;
+        if (messageBufferCopy[wrappedReadPos] != '\0') {
+            jstring message = env->NewStringUTF(messageBufferCopy + wrappedReadPos);
+            env->SetObjectArrayElement(result, arrayIndex++, message);
+            lastReadPos += env->GetStringLength(message) + 1;
+            env->DeleteLocalRef(message);
+        } else {
+            // Skip to the beginning of the buffer.
+            lastReadPos = (lastReadPos / messageBufferSize + 1) * messageBufferSize;
+        }
+    }
+    return result;
+}
+
+} // namespace nativemididemo
diff --git a/media/tests/NativeMidiDemo/jni/messagequeue.h b/media/tests/NativeMidiDemo/jni/messagequeue.h
new file mode 100644
index 0000000..20aa9e8
--- /dev/null
+++ b/media/tests/NativeMidiDemo/jni/messagequeue.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef NATIVEMIDIDEMO_MESSAGEQUEUE_H
+#define NATIVEMIDIDEMO_MESSAGEQUEUE_H
+
+#include <jni.h>
+
+namespace nativemididemo {
+
+void writeMessage(const char* message);
+jobjectArray getRecentMessagesForJava(JNIEnv* env, jobject thiz);
+
+} // namespace nativemididemo
+
+#endif // NATIVEMIDIDEMO_MESSAGEQUEUE_H
diff --git a/media/tests/NativeMidiDemo/jni/nativemidi-jni.cpp b/media/tests/NativeMidiDemo/jni/nativemidi-jni.cpp
new file mode 100644
index 0000000..8aa874a
--- /dev/null
+++ b/media/tests/NativeMidiDemo/jni/nativemidi-jni.cpp
@@ -0,0 +1,285 @@
+#include <atomic>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <jni.h>
+
+#include <midi/midi.h>
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+
+#include "messagequeue.h"
+
+extern "C" {
+JNIEXPORT jstring JNICALL Java_com_example_android_nativemididemo_NativeMidi_initAudio(
+        JNIEnv* env, jobject thiz, jint sampleRate, jint playSamples);
+JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_pauseAudio(
+        JNIEnv* env, jobject thiz);
+JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_resumeAudio(
+        JNIEnv* env, jobject thiz);
+JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_shutdownAudio(
+        JNIEnv* env, jobject thiz);
+JNIEXPORT jlong JNICALL Java_com_example_android_nativemididemo_NativeMidi_getPlaybackCounter(
+        JNIEnv* env, jobject thiz);
+JNIEXPORT jobjectArray JNICALL Java_com_example_android_nativemididemo_NativeMidi_getRecentMessages(
+        JNIEnv* env, jobject thiz);
+JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_startReadingMidi(
+        JNIEnv* env, jobject thiz, jint deviceId, jint portNumber);
+JNIEXPORT void JNICALL Java_com_example_android_nativemididemo_NativeMidi_stopReadingMidi(
+        JNIEnv* env, jobject thiz);
+}
+
+static const char* errStrings[] = {
+    "SL_RESULT_SUCCESS",                    // 0
+    "SL_RESULT_PRECONDITIONS_VIOLATED",     // 1
+    "SL_RESULT_PARAMETER_INVALID",          // 2
+    "SL_RESULT_MEMORY_FAILURE",             // 3
+    "SL_RESULT_RESOURCE_ERROR",             // 4
+    "SL_RESULT_RESOURCE_LOST",              // 5
+    "SL_RESULT_IO_ERROR",                   // 6
+    "SL_RESULT_BUFFER_INSUFFICIENT",        // 7
+    "SL_RESULT_CONTENT_CORRUPTED",          // 8
+    "SL_RESULT_CONTENT_UNSUPPORTED",        // 9
+    "SL_RESULT_CONTENT_NOT_FOUND",          // 10
+    "SL_RESULT_PERMISSION_DENIED",          // 11
+    "SL_RESULT_FEATURE_UNSUPPORTED",        // 12
+    "SL_RESULT_INTERNAL_ERROR",             // 13
+    "SL_RESULT_UNKNOWN_ERROR",              // 14
+    "SL_RESULT_OPERATION_ABORTED",          // 15
+    "SL_RESULT_CONTROL_LOST" };             // 16
+static const char* getSLErrStr(int code) {
+    return errStrings[code];
+}
+
+static SLObjectItf engineObject;
+static SLEngineItf engineEngine;
+static SLObjectItf outputMixObject;
+static SLObjectItf playerObject;
+static SLPlayItf playerPlay;
+static SLAndroidSimpleBufferQueueItf playerBufferQueue;
+
+static const int minPlaySamples = 32;
+static const int maxPlaySamples = 1000;
+static std::atomic_int playSamples(maxPlaySamples);
+static short playBuffer[maxPlaySamples];
+
+static std::atomic_ullong sharedCounter;
+
+static AMIDI_Device midiDevice = AMIDI_INVALID_HANDLE;
+static std::atomic<AMIDI_OutputPort> midiOutputPort(AMIDI_INVALID_HANDLE);
+
+static int setPlaySamples(int newPlaySamples)
+{
+    if (newPlaySamples < minPlaySamples) newPlaySamples = minPlaySamples;
+    if (newPlaySamples > maxPlaySamples) newPlaySamples = maxPlaySamples;
+    playSamples.store(newPlaySamples);
+    return newPlaySamples;
+}
+
+// Amount of messages we are ready to handle during one callback cycle.
+static const size_t MAX_INCOMING_MIDI_MESSAGES = 20;
+// Static allocation to save time in the callback.
+static AMIDI_Message incomingMidiMessages[MAX_INCOMING_MIDI_MESSAGES];
+
+static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void */*context*/)
+{
+    sharedCounter++;
+
+    AMIDI_OutputPort outputPort = midiOutputPort.load();
+    if (outputPort != AMIDI_INVALID_HANDLE) {
+        char midiDumpBuffer[1024];
+        ssize_t midiReceived = AMIDI_receive(
+                outputPort, incomingMidiMessages, MAX_INCOMING_MIDI_MESSAGES);
+        if (midiReceived >= 0) {
+            for (ssize_t i = 0; i < midiReceived; ++i) {
+                AMIDI_Message* msg = &incomingMidiMessages[i];
+                if (msg->opcode == AMIDI_OPCODE_DATA) {
+                    memset(midiDumpBuffer, 0, sizeof(midiDumpBuffer));
+                    int pos = snprintf(midiDumpBuffer, sizeof(midiDumpBuffer),
+                            "%" PRIx64 " ", msg->timestamp);
+                    for (uint8_t *b = msg->buffer, *e = b + msg->len; b < e; ++b) {
+                        pos += snprintf(midiDumpBuffer + pos, sizeof(midiDumpBuffer) - pos,
+                                "%02x ", *b);
+                    }
+                    nativemididemo::writeMessage(midiDumpBuffer);
+                } else if (msg->opcode == AMIDI_OPCODE_FLUSH) {
+                    nativemididemo::writeMessage("MIDI flush");
+                }
+            }
+        } else {
+            snprintf(midiDumpBuffer, sizeof(midiDumpBuffer),
+                    "! MIDI Receive error: %s !", strerror(-midiReceived));
+            nativemididemo::writeMessage(midiDumpBuffer);
+        }
+    }
+
+    size_t usedBufferSize = playSamples.load() * sizeof(playBuffer[0]);
+    if (usedBufferSize > sizeof(playBuffer)) {
+        usedBufferSize = sizeof(playBuffer);
+    }
+    (*bq)->Enqueue(bq, playBuffer, usedBufferSize);
+}
+
+jstring Java_com_example_android_nativemididemo_NativeMidi_initAudio(
+        JNIEnv* env, jobject, jint sampleRate, jint playSamples) {
+    const char* stage;
+    SLresult result;
+    char printBuffer[1024];
+
+    playSamples = setPlaySamples(playSamples);
+
+    result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
+    if (SL_RESULT_SUCCESS != result) { stage = "slCreateEngine"; goto handle_error; }
+
+    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
+    if (SL_RESULT_SUCCESS != result) { stage = "realize Engine object"; goto handle_error; }
+
+    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
+    if (SL_RESULT_SUCCESS != result) { stage = "get Engine interface"; goto handle_error; }
+
+    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
+    if (SL_RESULT_SUCCESS != result) { stage = "CreateOutputMix"; goto handle_error; }
+
+    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
+    if (SL_RESULT_SUCCESS != result) { stage = "realize OutputMix object"; goto handle_error; }
+
+    {
+    SLDataFormat_PCM format_pcm = { SL_DATAFORMAT_PCM, 1, (SLuint32)sampleRate * 1000,
+                                    SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
+                                    SL_SPEAKER_FRONT_LEFT, SL_BYTEORDER_LITTLEENDIAN };
+    SLDataLocator_AndroidSimpleBufferQueue loc_bufq =
+            { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 };
+    SLDataSource audioSrc = { &loc_bufq, &format_pcm };
+    SLDataLocator_OutputMix loc_outmix = { SL_DATALOCATOR_OUTPUTMIX, outputMixObject };
+    SLDataSink audioSnk = { &loc_outmix, NULL };
+    const SLInterfaceID ids[1] = { SL_IID_BUFFERQUEUE };
+    const SLboolean req[1] = { SL_BOOLEAN_TRUE };
+    result = (*engineEngine)->CreateAudioPlayer(
+            engineEngine, &playerObject, &audioSrc, &audioSnk, 1, ids, req);
+    if (SL_RESULT_SUCCESS != result) { stage = "CreateAudioPlayer"; goto handle_error; }
+
+    result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);
+    if (SL_RESULT_SUCCESS != result) { stage = "realize Player object"; goto handle_error; }
+    }
+
+    result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay);
+    if (SL_RESULT_SUCCESS != result) { stage = "get Play interface"; goto handle_error; }
+
+    result = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE, &playerBufferQueue);
+    if (SL_RESULT_SUCCESS != result) { stage = "get BufferQueue interface"; goto handle_error; }
+
+    result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue, bqPlayerCallback, NULL);
+    if (SL_RESULT_SUCCESS != result) { stage = "register BufferQueue callback"; goto handle_error; }
+
+    result = (*playerBufferQueue)->Enqueue(playerBufferQueue, playBuffer, sizeof(playBuffer));
+    if (SL_RESULT_SUCCESS != result) {
+        stage = "enqueue into PlayerBufferQueue"; goto handle_error; }
+
+    result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);
+    if (SL_RESULT_SUCCESS != result) {
+        stage = "SetPlayState(SL_PLAYSTATE_PLAYING)"; goto handle_error; }
+
+    snprintf(printBuffer, sizeof(printBuffer),
+            "Success, sample rate %d, buffer samples %d", sampleRate, playSamples);
+    return env->NewStringUTF(printBuffer);
+
+handle_error:
+    snprintf(printBuffer, sizeof(printBuffer), "Error at %s: %s", stage, getSLErrStr(result));
+    return env->NewStringUTF(printBuffer);
+}
+
+void Java_com_example_android_nativemididemo_NativeMidi_pauseAudio(
+        JNIEnv*, jobject) {
+    if (playerPlay != NULL) {
+        (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PAUSED);
+    }
+}
+
+void Java_com_example_android_nativemididemo_NativeMidi_resumeAudio(
+        JNIEnv*, jobject) {
+    if (playerBufferQueue != NULL && playerPlay != NULL) {
+        (*playerBufferQueue)->Enqueue(playerBufferQueue, playBuffer, sizeof(playBuffer));
+        (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);
+    }
+}
+
+void Java_com_example_android_nativemididemo_NativeMidi_shutdownAudio(
+        JNIEnv*, jobject) {
+    if (playerObject != NULL) {
+        (*playerObject)->Destroy(playerObject);
+        playerObject = NULL;
+        playerPlay = NULL;
+        playerBufferQueue = NULL;
+    }
+
+    if (outputMixObject != NULL) {
+        (*outputMixObject)->Destroy(outputMixObject);
+        outputMixObject = NULL;
+    }
+
+    if (engineObject != NULL) {
+        (*engineObject)->Destroy(engineObject);
+        engineObject = NULL;
+        engineEngine = NULL;
+    }
+}
+
+jlong Java_com_example_android_nativemididemo_NativeMidi_getPlaybackCounter(JNIEnv*, jobject) {
+    return sharedCounter.load();
+}
+
+jobjectArray Java_com_example_android_nativemididemo_NativeMidi_getRecentMessages(
+        JNIEnv* env, jobject thiz) {
+    return nativemididemo::getRecentMessagesForJava(env, thiz);
+}
+
+void Java_com_example_android_nativemididemo_NativeMidi_startReadingMidi(
+        JNIEnv*, jobject, jint deviceId, jint portNumber) {
+    char buffer[1024];
+
+    int result = AMIDI_getDeviceById(deviceId, &midiDevice);
+    if (result == 0) {
+        snprintf(buffer, sizeof(buffer), "Obtained device token for uid %d: token %d", deviceId, midiDevice);
+    } else {
+        snprintf(buffer, sizeof(buffer), "Could not obtain device token for uid %d: %d", deviceId, result);
+    }
+    nativemididemo::writeMessage(buffer);
+    if (result) return;
+
+    AMIDI_DeviceInfo deviceInfo;
+    result = AMIDI_getDeviceInfo(midiDevice, &deviceInfo);
+    if (result == 0) {
+        snprintf(buffer, sizeof(buffer), "Device info: uid %d, type %d, priv %d, ports %d I / %d O",
+                deviceInfo.uid, deviceInfo.type, deviceInfo.isPrivate,
+                (int)deviceInfo.inputPortCount, (int)deviceInfo.outputPortCount);
+    } else {
+        snprintf(buffer, sizeof(buffer), "Could not obtain device info %d", result);
+    }
+    nativemididemo::writeMessage(buffer);
+    if (result) return;
+
+    AMIDI_OutputPort outputPort;
+    result = AMIDI_openOutputPort(midiDevice, portNumber, &outputPort);
+    if (result == 0) {
+        snprintf(buffer, sizeof(buffer), "Opened port %d: token %d", portNumber, outputPort);
+        midiOutputPort.store(outputPort);
+    } else {
+        snprintf(buffer, sizeof(buffer), "Could not open port %d: %d", deviceId, result);
+    }
+    nativemididemo::writeMessage(buffer);
+}
+
+void Java_com_example_android_nativemididemo_NativeMidi_stopReadingMidi(
+        JNIEnv*, jobject) {
+    AMIDI_OutputPort outputPort = midiOutputPort.exchange(AMIDI_INVALID_HANDLE);
+    if (outputPort == AMIDI_INVALID_HANDLE) return;
+    int result = AMIDI_closeOutputPort(outputPort);
+    char buffer[1024];
+    if (result == 0) {
+        snprintf(buffer, sizeof(buffer), "Closed port by token %d", outputPort);
+    } else {
+        snprintf(buffer, sizeof(buffer), "Could not close port by token %d: %d", outputPort, result);
+    }
+    nativemididemo::writeMessage(buffer);
+}
diff --git a/media/tests/NativeMidiDemo/res/layout/main.xml b/media/tests/NativeMidiDemo/res/layout/main.xml
new file mode 100644
index 0000000..465d471
--- /dev/null
+++ b/media/tests/NativeMidiDemo/res/layout/main.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+  <LinearLayout
+      android:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      >
+    <TextView
+        android:id="@+id/callback_status"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="13sp"
+        android:typeface="monospace"
+        />
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="13sp"
+        android:typeface="monospace"
+        android:text=" | "
+        />
+    <TextView
+        android:id="@+id/java_midi_status"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="13sp"
+        android:typeface="monospace"
+        />
+    <TextView
+        android:layout_width="0px"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:textSize="13sp"
+        android:typeface="monospace"
+        android:text=" "
+        />
+  </LinearLayout>
+  <HorizontalScrollView
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:paddingTop="6sp"
+      android:paddingBottom="6sp"
+      >
+    <RadioGroup
+        android:id="@+id/devices"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:checkedButton="@+id/device_none"
+        >
+      <RadioButton
+          android:id="@+id/device_none"
+          android:text="None" 
+          />
+    </RadioGroup>
+  </HorizontalScrollView>
+  <com.example.android.nativemididemo.TouchableScrollView android:id="@+id/messages_scroll"
+              android:layout_width="match_parent"
+              android:layout_height="0px"
+              android:layout_weight="1"
+              >
+    <TextView android:id="@+id/messages"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:textSize="13sp"
+              android:typeface="monospace"
+              />
+  </com.example.android.nativemididemo.TouchableScrollView>
+  <LinearLayout
+      android:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      >
+    <Button android:id="@+id/clear_messages"
+          android:layout_width="0px"
+          android:layout_height="wrap_content"
+          android:layout_weight="1"
+          android:text="@string/clear_messages"
+          android:onClick="onClearMessages"
+          />
+    <Button android:id="@+id/close_port"
+          android:layout_width="0px"
+          android:layout_height="wrap_content"
+          android:layout_weight="1"
+          android:text="@string/close_port"
+          android:onClick="onClosePort"
+          />
+  </LinearLayout>
+</LinearLayout>
diff --git a/media/tests/NativeMidiDemo/res/mipmap-hdpi/ic_launcher.png b/media/tests/NativeMidiDemo/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/media/tests/NativeMidiDemo/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/media/tests/NativeMidiDemo/res/mipmap-mdpi/ic_launcher.png b/media/tests/NativeMidiDemo/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/media/tests/NativeMidiDemo/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/media/tests/NativeMidiDemo/res/mipmap-xhdpi/ic_launcher.png b/media/tests/NativeMidiDemo/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/media/tests/NativeMidiDemo/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/media/tests/NativeMidiDemo/res/mipmap-xxhdpi/ic_launcher.png b/media/tests/NativeMidiDemo/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/media/tests/NativeMidiDemo/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/media/tests/NativeMidiDemo/res/values/strings.xml b/media/tests/NativeMidiDemo/res/values/strings.xml
new file mode 100644
index 0000000..5b69b52
--- /dev/null
+++ b/media/tests/NativeMidiDemo/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <string name="app_name">NativeMidiDemo</string>
+  <string name="clear_messages">Clear Messages</string>
+  <string name="close_port">Close Port</string>
+</resources>
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 3ede2ee..1b1f28c 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -139,16 +139,27 @@
     ANativeActivity_setWindowFlags;
     ANativeActivity_setWindowFormat;
     ANativeActivity_showSoftInput;
-    ANativeWindow_acquire; # removed=26
-    ANativeWindow_fromSurface; # removed=26
-    ANativeWindow_fromSurfaceTexture; # removed=26
-    ANativeWindow_getFormat; # removed=26
-    ANativeWindow_getHeight; # removed=26
-    ANativeWindow_getWidth; # removed=26
-    ANativeWindow_lock; # removed=26
-    ANativeWindow_release; # removed=26
-    ANativeWindow_setBuffersGeometry; # removed=26
-    ANativeWindow_unlockAndPost; # removed=26
+    AHardwareBuffer_acquire; # introduced=26
+    AHardwareBuffer_allocate; # introduced=26
+    AHardwareBuffer_describe; # introduced=26
+    AHardwareBuffer_fromHardwareBuffer; # introduced=26
+    AHardwareBuffer_getNativeHandle; # introduced=26
+    AHardwareBuffer_lock; # introduced=26
+    AHardwareBuffer_recvHandleFromUnixSocket; # introduced=26
+    AHardwareBuffer_release; # introduced=26
+    AHardwareBuffer_sendHandleToUnixSocket; # introduced=26
+    AHardwareBuffer_toHardwareBuffer; # introduced=26
+    AHardwareBuffer_unlock; # introduced=26
+    ANativeWindow_acquire;
+    ANativeWindow_fromSurface;
+    ANativeWindow_fromSurfaceTexture; # introduced-arm=13 introduced-mips=13 introduced-x86=13
+    ANativeWindow_getFormat;
+    ANativeWindow_getHeight;
+    ANativeWindow_getWidth;
+    ANativeWindow_lock;
+    ANativeWindow_release;
+    ANativeWindow_setBuffersGeometry;
+    ANativeWindow_unlockAndPost;
     AObbInfo_delete;
     AObbInfo_getFlags;
     AObbInfo_getPackageName;
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
index 6d2de98..bf5cabb 100644
--- a/native/graphics/jni/bitmap.cpp
+++ b/native/graphics/jni/bitmap.cpp
@@ -15,11 +15,7 @@
  */
 
 #include <android/bitmap.h>
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-#include <GraphicsJNI.h>
-#pragma GCC diagnostic pop
+#include <Bitmap.h>
 
 int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
                           AndroidBitmapInfo* info) {
@@ -27,32 +23,8 @@
         return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
     }
 
-    SkBitmap bm;
-    GraphicsJNI::getSkBitmap(env, jbitmap, &bm);
-
     if (info) {
-        info->width     = bm.width();
-        info->height    = bm.height();
-        info->stride    = bm.rowBytes();
-        info->flags     = 0;
-
-        switch (bm.colorType()) {
-            case kN32_SkColorType:
-                info->format = ANDROID_BITMAP_FORMAT_RGBA_8888;
-                break;
-            case kRGB_565_SkColorType:
-                info->format = ANDROID_BITMAP_FORMAT_RGB_565;
-                break;
-            case kARGB_4444_SkColorType:
-                info->format = ANDROID_BITMAP_FORMAT_RGBA_4444;
-                break;
-            case kAlpha_8_SkColorType:
-                info->format = ANDROID_BITMAP_FORMAT_A_8;
-                break;
-            default:
-                info->format = ANDROID_BITMAP_FORMAT_NONE;
-                break;
-        }
+        android::bitmap::imageInfo(env, jbitmap, info);
     }
     return ANDROID_BITMAP_RESULT_SUCCESS;
 }
@@ -62,19 +34,11 @@
         return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
     }
 
-    SkPixelRef* pixelRef = GraphicsJNI::refSkPixelRef(env, jbitmap);
-    if (!pixelRef) {
+    void* addr = android::bitmap::lockPixels(env, jbitmap);
+    if (!addr) {
         return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
     }
 
-    pixelRef->lockPixels();
-    void* addr = pixelRef->pixels();
-    if (NULL == addr) {
-        pixelRef->unlockPixels();
-        pixelRef->unref();
-        return ANDROID_BITMAP_RESULT_ALLOCATION_FAILED;
-    }
-
     if (addrPtr) {
         *addrPtr = addr;
     }
@@ -86,26 +50,10 @@
         return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
     }
 
-    SkPixelRef* pixelRef = GraphicsJNI::refSkPixelRef(env, jbitmap);
-    if (!pixelRef) {
+    bool unlocked = android::bitmap::unlockPixels(env, jbitmap);
+    if (!unlocked) {
         return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
     }
-
-    // notifyPixelsChanged() needs be called to apply writes to GL-backed
-    // bitmaps.  Note that this will slow down read-only accesses to the
-    // bitmaps, but the NDK methods are primarily intended to be used for
-    // writes.
-    pixelRef->notifyPixelsChanged();
-
-    pixelRef->unlockPixels();
-    // Awkward in that we need to double-unref as the call to get the SkPixelRef
-    // did a ref(), so we need to unref() for the local ref and for the previous
-    // AndroidBitmap_lockPixels(). However this keeps GraphicsJNI a bit safer
-    // if others start using it without knowing about android::Bitmap's "fun"
-    // ref counting mechanism(s).
-    pixelRef->unref();
-    pixelRef->unref();
-
     return ANDROID_BITMAP_RESULT_SUCCESS;
 }
 
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java b/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
index 84745b2..19c2c14 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/ReceiverActivity.java
@@ -46,8 +46,8 @@
                 final Uri uri = DocumentsContract.buildRootUri(
                         MtpDocumentsProvider.AUTHORITY, deviceRootId);
 
-                final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE);
-                intent.setData(uri);
+                final Intent intent = new Intent(Intent.ACTION_VIEW);
+                intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM);
                 intent.addCategory(Intent.CATEGORY_DEFAULT);
                 this.startActivity(intent);
             } catch (IOException exception) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index 7f7249f0..b06b032 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -28,6 +28,7 @@
 import android.util.Log;
 
 import com.android.settingslib.R;
+import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -35,6 +36,13 @@
 public class AppUtils {
     private static final String TAG = "AppUtils";
 
+    /**
+     * This should normally only be set in robolectric tests, to avoid getting a method not found
+     * exception when calling the isInstantApp method of the ApplicationInfo class, because
+     * robolectric does not yet have an implementation of it.
+     */
+    private static InstantAppDataProvider sInstantAppDataProvider = null;
+
     public static CharSequence getLaunchByDefaultSummary(ApplicationsState.AppEntry appEntry,
             IUsbManager usbManager, PackageManager pm, Context context) {
         String packageName = appEntry.info.packageName;
@@ -74,7 +82,11 @@
      * Returns a boolean indicating whether the given package should be considered an instant app
      */
     public static boolean isInstant(ApplicationInfo info) {
-        if (info.isInstantApp()) {
+        if (sInstantAppDataProvider != null) {
+            if (sInstantAppDataProvider.isInstantApp(info)) {
+                return true;
+            }
+        } else if (info.isInstantApp()) {
             return true;
         }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
new file mode 100644
index 0000000..ec8e956
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/PermissionsSummaryHelper.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.applications;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.pm.permission.RuntimePermissionPresentationInfo;
+import android.content.pm.permission.RuntimePermissionPresenter;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class PermissionsSummaryHelper  {
+
+    public static void getPermissionSummary(Context context, String pkg,
+            final PermissionsResultCallback callback) {
+        final RuntimePermissionPresenter presenter =
+                RuntimePermissionPresenter.getInstance(context);
+        presenter.getAppPermissions(pkg, new RuntimePermissionPresenter.OnResultCallback() {
+            @Override
+            public void onGetAppPermissions(
+                    @NonNull List<RuntimePermissionPresentationInfo> permissions) {
+                final int permissionCount = permissions.size();
+
+                int grantedStandardCount = 0;
+                int grantedAdditionalCount = 0;
+                int requestedCount = 0;
+                List<CharSequence> grantedStandardLabels = new ArrayList<>();
+
+                for (int i = 0; i < permissionCount; i++) {
+                    RuntimePermissionPresentationInfo permission = permissions.get(i);
+                    requestedCount++;
+                    if (permission.isGranted()) {
+                        if (permission.isStandard()) {
+                            grantedStandardLabels.add(permission.getLabel());
+                            grantedStandardCount++;
+                        } else {
+                            grantedAdditionalCount++;
+                        }
+                    }
+                }
+
+                Collator collator = Collator.getInstance();
+                collator.setStrength(Collator.PRIMARY);
+                Collections.sort(grantedStandardLabels, collator);
+
+                callback.onPermissionSummaryResult(grantedStandardCount, requestedCount,
+                        grantedAdditionalCount, grantedStandardLabels);
+            }
+        }, null);
+    }
+
+    public static abstract class PermissionsResultCallback {
+        public void onAppWithPermissionsCountsResult(int standardGrantedPermissionAppCount,
+                int standardUsedPermissionAppCount) {
+            /* do nothing - stub */
+        }
+
+        public void onPermissionSummaryResult(int standardGrantedPermissionCount,
+                int requestedPermissionCount, int additionalGrantedPermissionCount,
+                List<CharSequence> grantedGroupLabels) {
+            /* do nothing - stub */
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/instantapps/InstantAppDataProvider.java b/packages/SettingsLib/src/com/android/settingslib/applications/instantapps/InstantAppDataProvider.java
new file mode 100644
index 0000000..8b15715
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/instantapps/InstantAppDataProvider.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.applications.instantapps;
+
+import android.content.pm.ApplicationInfo;
+
+/**
+ * This helps deal with the fact that robolectric does not yet have an implementation of the
+ * isInstantApp method of ApplicationInfo, so we get a method not found exception when running tests
+ * if we try to call it directly.
+ */
+public interface InstantAppDataProvider {
+    public boolean isInstantApp(ApplicationInfo info);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 3df7e66..50972c7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -223,7 +223,8 @@
         }
 
         final Context context = getContext();
-        int level = WifiManager.calculateSignalLevel(mAccessPoint.getRssi(), 5 /* levels */);
+        int level = WifiManager.calculateSignalLevel(
+                mAccessPoint.getRssi(), WifiManager.RSSI_LEVELS);
         int wifiBadge = mAccessPoint.getBadge();
         if (level != mLevel || wifiBadge != mWifiBadge) {
             mLevel = level;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 56ae618..de79d3f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -143,6 +143,8 @@
     @GuardedBy("sLock")
     private static Signature sSystemSignature;
 
+    private final Object mWriteLock = new Object();
+
     private final Object mLock;
 
     private final Handler mHandler;
@@ -551,62 +553,66 @@
     }
 
     private void doWriteState() {
-        if (DEBUG_PERSISTENCE) {
-            Slog.i(LOG_TAG, "[PERSIST START]");
-        }
-
-        AtomicFile destination = new AtomicFile(mStatePersistFile);
-
-        final int version;
-        final ArrayMap<String, Setting> settings;
-
-        synchronized (mLock) {
-            version = mVersion;
-            settings = new ArrayMap<>(mSettings);
-            mDirty = false;
-            mWriteScheduled = false;
-        }
-
-        FileOutputStream out = null;
-        try {
-            out = destination.startWrite();
-
-            XmlSerializer serializer = Xml.newSerializer();
-            serializer.setOutput(out, StandardCharsets.UTF_8.name());
-            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-            serializer.startDocument(null, true);
-            serializer.startTag(null, TAG_SETTINGS);
-            serializer.attribute(null, ATTR_VERSION, String.valueOf(version));
-
-            final int settingCount = settings.size();
-            for (int i = 0; i < settingCount; i++) {
-                Setting setting = settings.valueAt(i);
-
-                writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),
-                        setting.getValue(), setting.getDefaultValue(), setting.getPackageName(),
-                        setting.getTag(), setting.isDefaultFromSystem());
-
-                if (DEBUG_PERSISTENCE) {
-                    Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "=" + setting.getValue());
-                }
+        synchronized (mWriteLock) {
+            if (DEBUG_PERSISTENCE) {
+                Slog.i(LOG_TAG, "[PERSIST START]");
             }
 
-            serializer.endTag(null, TAG_SETTINGS);
-            serializer.endDocument();
-            destination.finishWrite(out);
+            AtomicFile destination = new AtomicFile(mStatePersistFile);
+
+            final int version;
+            final ArrayMap<String, Setting> settings;
 
             synchronized (mLock) {
-                addHistoricalOperationLocked(HISTORICAL_OPERATION_PERSIST, null);
+                version = mVersion;
+                settings = new ArrayMap<>(mSettings);
+                mDirty = false;
+                mWriteScheduled = false;
             }
 
-            if (DEBUG_PERSISTENCE) {
-                Slog.i(LOG_TAG, "[PERSIST END]");
+            FileOutputStream out = null;
+            try {
+                out = destination.startWrite();
+
+                XmlSerializer serializer = Xml.newSerializer();
+                serializer.setOutput(out, StandardCharsets.UTF_8.name());
+                serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
+                        true);
+                serializer.startDocument(null, true);
+                serializer.startTag(null, TAG_SETTINGS);
+                serializer.attribute(null, ATTR_VERSION, String.valueOf(version));
+
+                final int settingCount = settings.size();
+                for (int i = 0; i < settingCount; i++) {
+                    Setting setting = settings.valueAt(i);
+
+                    writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),
+                            setting.getValue(), setting.getDefaultValue(), setting.getPackageName(),
+                            setting.getTag(), setting.isDefaultFromSystem());
+
+                    if (DEBUG_PERSISTENCE) {
+                        Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "="
+                                + setting.getValue());
+                    }
+                }
+
+                serializer.endTag(null, TAG_SETTINGS);
+                serializer.endDocument();
+                destination.finishWrite(out);
+
+                synchronized (mLock) {
+                    addHistoricalOperationLocked(HISTORICAL_OPERATION_PERSIST, null);
+                }
+
+                if (DEBUG_PERSISTENCE) {
+                    Slog.i(LOG_TAG, "[PERSIST END]");
+                }
+            } catch (Throwable t) {
+                Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
+                destination.failWrite(out);
+            } finally {
+                IoUtils.closeQuietly(out);
             }
-        } catch (Throwable t) {
-            Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
-            destination.failWrite(out);
-        } finally {
-            IoUtils.closeQuietly(out);
         }
     }
 
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
index ddee89e..674ed5a 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
@@ -19,7 +19,7 @@
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
-@ProvidesInterface(action = NavGesture.ACTION, version = NavBarButtonProvider.VERSION)
+@ProvidesInterface(action = NavGesture.ACTION, version = NavGesture.VERSION)
 public interface NavGesture extends Plugin {
 
     public static final String ACTION = "com.android.systemui.action.PLUGIN_NAV_GESTURE";
diff --git a/packages/SystemUI/src/com/android/systemui/SystemBars.java b/packages/SystemUI/src/com/android/systemui/SystemBars.java
index 6623cabe..b5093b3 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemBars.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemBars.java
@@ -43,13 +43,6 @@
     }
 
     @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        if (mStatusBar != null) {
-            mStatusBar.onConfigurationChanged(newConfig);
-        }
-    }
-
-    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mStatusBar != null) {
             mStatusBar.dump(fd, pw, args);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index ae402ef..e7256d1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -71,8 +71,15 @@
         }
 
         @Override
+        public void onPinnedStackAnimationStarted() {
+            // Disable touches while the animation is running
+            mTouchHandler.setTouchEnabled(false);
+        }
+
+        @Override
         public void onPinnedStackAnimationEnded() {
-            // TODO(winsonc): Disable touch interaction with the PiP until the animation ends
+            // Re-enable touches after the animation completes
+            mTouchHandler.setTouchEnabled(true);
         }
 
         @Override
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 d832810..4100b66 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -181,6 +181,10 @@
         registerInputConsumer();
     }
 
+    public void setTouchEnabled(boolean enabled) {
+        mTouchState.setAllowTouches(enabled);
+    }
+
     public void onActivityPinned() {
         // Reset some states once we are pinned
         if (mIsTappingThrough) {
@@ -294,6 +298,7 @@
                 // Fall through to clean up
             }
             case MotionEvent.ACTION_CANCEL: {
+                mTouchState.reset();
                 break;
             }
         }
@@ -418,6 +423,10 @@
 
         @Override
         public void onDown(PipTouchState touchState) {
+            if (!touchState.isUserInteracting()) {
+                return;
+            }
+
             if (ENABLE_DRAG_TO_DISMISS) {
                 mDismissViewController.createDismissTarget();
                 mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
@@ -426,6 +435,10 @@
 
         @Override
         boolean onMove(PipTouchState touchState) {
+            if (!touchState.isUserInteracting()) {
+                return false;
+            }
+
             if (touchState.startedDragging()) {
                 mSavedSnapFraction = -1f;
             }
@@ -458,6 +471,10 @@
 
         @Override
         public boolean onUp(PipTouchState touchState) {
+            if (!touchState.isUserInteracting()) {
+                return false;
+            }
+
             try {
                 if (ENABLE_DRAG_TO_DISMISS) {
                     mHandler.removeCallbacks(mShowDismissAffordance);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index 702ad0a..a317dc3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -37,6 +37,7 @@
     private final PointF mLastTouch = new PointF();
     private final PointF mLastDelta = new PointF();
     private final PointF mVelocity = new PointF();
+    private boolean mAllowTouches = true;
     private boolean mIsUserInteracting = false;
     private boolean mIsDragging = false;
     private boolean mStartedDragging = false;
@@ -48,23 +49,41 @@
     }
 
     /**
+     * Resets this state.
+     */
+    public void reset() {
+        mAllowDraggingOffscreen = false;
+        mIsDragging = false;
+        mStartedDragging = false;
+        mIsUserInteracting = false;
+    }
+
+    /**
      * Processess a given touch event and updates the state.
      */
     public void onTouchEvent(MotionEvent ev) {
         switch (ev.getAction()) {
             case MotionEvent.ACTION_DOWN: {
+                if (!mAllowTouches) {
+                    return;
+                }
+
                 // Initialize the velocity tracker
                 initOrResetVelocityTracker();
+
                 mActivePointerId = ev.getPointerId(0);
                 mLastTouch.set(ev.getX(), ev.getY());
                 mDownTouch.set(mLastTouch);
-                mIsDragging = false;
-                mStartedDragging = false;
                 mAllowDraggingOffscreen = true;
                 mIsUserInteracting = true;
                 break;
             }
             case MotionEvent.ACTION_MOVE: {
+                // Skip event if we did not start processing this touch gesture
+                if (!mIsUserInteracting) {
+                    break;
+                }
+
                 // Update the velocity tracker
                 mVelocityTracker.addMovement(ev);
                 int pointerIndex = ev.findPointerIndex(mActivePointerId);
@@ -86,6 +105,11 @@
                 break;
             }
             case MotionEvent.ACTION_POINTER_UP: {
+                // Skip event if we did not start processing this touch gesture
+                if (!mIsUserInteracting) {
+                    break;
+                }
+
                 // Update the velocity tracker
                 mVelocityTracker.addMovement(ev);
 
@@ -100,6 +124,11 @@
                 break;
             }
             case MotionEvent.ACTION_UP: {
+                // Skip event if we did not start processing this touch gesture
+                if (!mIsUserInteracting) {
+                    break;
+                }
+
                 // Update the velocity tracker
                 mVelocityTracker.addMovement(ev);
                 mVelocityTracker.computeCurrentVelocity(1000,
@@ -112,7 +141,6 @@
                 // Fall through to clean up
             }
             case MotionEvent.ACTION_CANCEL: {
-                mIsUserInteracting = false;
                 recycleVelocityTracker();
                 break;
             }
@@ -171,6 +199,19 @@
     }
 
     /**
+     * Sets whether touching is currently allowed.
+     */
+    public void setAllowTouches(boolean allowTouches) {
+        mAllowTouches = allowTouches;
+
+        // If the user happens to touch down before this is sent from the system during a transition
+        // then block any additional handling by resetting the state now
+        if (mIsUserInteracting) {
+            reset();
+        }
+    }
+
+    /**
      * Disallows dragging offscreen for the duration of the current gesture.
      */
     public void setDisallowDraggingOffscreen() {
@@ -202,6 +243,7 @@
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
+        pw.println(innerPrefix + "mAllowTouches=" + mAllowTouches);
         pw.println(innerPrefix + "mDownTouch=" + mDownTouch);
         pw.println(innerPrefix + "mDownDelta=" + mDownDelta);
         pw.println(innerPrefix + "mLastTouch=" + mLastTouch);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 428fe9b..7d13f76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -84,7 +84,6 @@
         mLabel = (TextView) mLabelContainer.findViewById(R.id.tile_label);
         mPadLock = (ImageView) mLabelContainer.findViewById(R.id.restricted_padlock);
 
-        mLabelContainer.setBackground(newTileBackground());
         addView(mLabelContainer);
     }
 
@@ -102,6 +101,11 @@
             mLabel.setText(state.label);
         }
         mDivider.setVisibility(state.dualTarget ? View.VISIBLE : View.INVISIBLE);
+        if (state.dualTarget != mLabelContainer.isClickable()) {
+            mLabelContainer.setClickable(state.dualTarget);
+            mLabelContainer.setLongClickable(state.dualTarget);
+            mLabelContainer.setBackground(state.dualTarget ? newTileBackground() : null);
+        }
         mLabel.setEnabled(!state.disabledByPolicy);
         mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
     }
@@ -110,7 +114,9 @@
     public void init(OnClickListener click, OnClickListener secondaryClick,
             OnLongClickListener longClick) {
         super.init(click, secondaryClick, longClick);
-        mLabelContainer.setClickable(true);
         mLabelContainer.setOnClickListener(secondaryClick);
+        mLabelContainer.setOnLongClickListener(longClick);
+        mLabelContainer.setClickable(false);
+        mLabelContainer.setLongClickable(false);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index b28b0e7..a76299d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -288,7 +288,7 @@
             drawable = mTile.getIcon().loadDrawable(mContext);
         } catch (Exception e) {
             Log.w(TAG, "Invalid icon, forcing into unavailable state");
-            tileState = Tile.STATE_UNAVAILABLE;
+            state.state = Tile.STATE_UNAVAILABLE;
             drawable = mDefaultIcon.loadDrawable(mContext);
         }
         state.icon = new DrawableIcon(drawable);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index f7bfc1e..62aa466 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -23,16 +23,18 @@
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.RelativeSizeSpan;
+import android.util.Log;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.widget.Button;
 import android.widget.Checkable;
 import android.widget.ImageView;
+import android.widget.Switch;
 import android.widget.TextView;
+
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.BatteryInfo;
 import com.android.settingslib.graph.BatteryMeterDrawableBase;
@@ -43,9 +45,8 @@
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.statusbar.policy.BatteryController;
 
-import java.text.NumberFormat;
-
-public class BatteryTile extends QSTile<QSTile.State> implements BatteryController.BatteryStateChangeCallback {
+public class BatterySaverTile extends QSTile<QSTile.BooleanState> implements
+        BatteryController.BatteryStateChangeCallback {
 
     private final BatteryController mBatteryController;
     private final BatteryDetail mBatteryDetail = new BatteryDetail();
@@ -56,19 +57,14 @@
     private boolean mDetailShown;
     private boolean mPluggedIn;
 
-    public BatteryTile(Host host) {
+    public BatterySaverTile(Host host) {
         super(host);
         mBatteryController = Dependency.get(BatteryController.class);
     }
 
     @Override
-    public State newTileState() {
-        return new QSTile.State();
-    }
-
-    @Override
-    public DetailAdapter getDetailAdapter() {
-        return mBatteryDetail;
+    public BooleanState newTileState() {
+        return new BooleanState();
     }
 
     @Override
@@ -109,22 +105,15 @@
     }
 
     @Override
-    protected void handleUpdateState(State state, Object arg) {
-        int level = (arg != null) ? (Integer) arg : mLevel;
-        String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0);
-
+    protected void handleUpdateState(BooleanState state, Object arg) {
         state.state = mCharging ? Tile.STATE_UNAVAILABLE
                 : mPowerSave ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         state.icon = ResourceIcon.get(R.drawable.ic_qs_battery_saver);
         state.label = mContext.getString(R.string.battery_detail_switch_title);
-        state.contentDescription = mContext.getString(R.string.accessibility_quick_settings_battery,
-                percentage) + "," +
-                (mPowerSave ? mContext.getString(R.string.battery_saver_notification_title)
-                        : mCharging ? mContext.getString(R.string.expanded_header_battery_charging)
-                                : "")
-                + "," + mContext.getString(R.string.accessibility_battery_details);
+        state.contentDescription = state.label;
+        state.value = mPowerSave;
         state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
-                = Button.class.getName();
+                = Switch.class.getName();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index cda902b..1042356 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -154,6 +154,7 @@
         public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
         public void onActivityPinned() { }
         public void onPinnedActivityRestartAttempt(String launchedFromPackage) { }
+        public void onPinnedStackAnimationStarted() { }
         public void onPinnedStackAnimationEnded() { }
         public void onActivityForcedResizable(String packageName, int taskId) { }
         public void onActivityDismissingDockedStack() { }
@@ -206,6 +207,12 @@
         }
 
         @Override
+        public void onPinnedStackAnimationStarted() throws RemoteException {
+            mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_STARTED);
+            mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_STARTED);
+        }
+
+        @Override
         public void onPinnedStackAnimationEnded() throws RemoteException {
             mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED);
             mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED);
@@ -1219,6 +1226,7 @@
         private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6;
         private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7;
         private static final int ON_TASK_PROFILE_LOCKED = 8;
+        private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9;
 
         @Override
         public void handleMessage(Message msg) {
@@ -1248,6 +1256,12 @@
                     }
                     break;
                 }
+                case ON_PINNED_STACK_ANIMATION_STARTED: {
+                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                        mTaskStackListeners.get(i).onPinnedStackAnimationStarted();
+                    }
+                    break;
+                }
                 case ON_PINNED_STACK_ANIMATION_ENDED: {
                     for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                         mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 98787f7..d4997ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -923,12 +923,6 @@
         mOnActivatedListener = onActivatedListener;
     }
 
-    public void reset() {
-        setTintColor(0);
-        resetBackgroundAlpha();
-        setBelowSpeedBump(false);
-    }
-
     public boolean hasSameBgColor(ActivatableNotificationView otherView) {
         return calculateBgColor() == otherView.calculateBgColor();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index bfe4bb2..602c3df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.statusbar.notification.NotificationInflater.InflationExceptionHandler;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -42,6 +44,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Chronometer;
 import android.widget.ImageView;
+import android.widget.RemoteViews;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -52,6 +55,8 @@
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.NotificationInflater;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -70,6 +75,7 @@
 
     private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
     private static final int COLORED_DIVIDER_ALPHA = 0x7B;
+    private final NotificationInflater mNotificationInflater;
     private int mIconTransformContentShift;
     private int mIconTransformContentShiftNoIcon;
     private int mNotificationMinHeightLegacy;
@@ -152,14 +158,15 @@
     private OnClickListener mExpandClickListener = new OnClickListener() {
         @Override
         public void onClick(View v) {
-            if (!mShowingPublic && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
+            if (!mShowingPublic && (!mIsLowPriority || isExpanded())
+                    && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
                 mGroupExpansionChanging = true;
                 final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
                 boolean nowExpanded = mGroupManager.toggleGroupExpansion(mStatusBarNotification);
                 mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
                 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER,
                         nowExpanded);
-                logExpansionEvent(true /* userAction */, wasExpanded);
+                onExpansionChanged(true /* userAction */, wasExpanded);
             } else {
                 if (v.isAccessibilityFocused()) {
                     mPrivateLayout.setFocusOnVisibilityChange();
@@ -246,6 +253,7 @@
         }
         if (mIsSummaryWithChildren) {
             setIconAnimationRunningForChild(running, mChildrenContainer.getHeaderView());
+            setIconAnimationRunningForChild(running, mChildrenContainer.getLowPriorityHeaderView());
             List<ExpandableNotificationRow> notificationChildren =
                     mChildrenContainer.getNotificationChildren();
             for (int i = 0; i < notificationChildren.size(); i++) {
@@ -298,17 +306,22 @@
         }
     }
 
-    public void onNotificationUpdated(NotificationData.Entry entry) {
+    public void updateNotification(NotificationData.Entry entry) throws InflationException {
         mEntry = entry;
         mStatusBarNotification = entry.notification;
+        mNotificationInflater.inflateNotificationViews();
+        onNotificationUpdated();
+    }
+
+    private void onNotificationUpdated() {
         for (NotificationContentView l : mLayouts) {
-            l.onNotificationUpdated(entry);
+            l.onNotificationUpdated(mEntry);
         }
         mIsColorized = mStatusBarNotification.getNotification().isColorized();
         mShowingPublicInitialized = false;
         updateNotificationColor();
         if (mIsSummaryWithChildren) {
-            mChildrenContainer.recreateNotificationHeader(mExpandClickListener, mEntry.notification);
+            mChildrenContainer.recreateNotificationHeader(mExpandClickListener);
             mChildrenContainer.onNotificationUpdated();
         }
         if (mIconAnimationRunning) {
@@ -458,6 +471,9 @@
         boolean childInGroup = StatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
         mNotificationParent = childInGroup ? parent : null;
         mPrivateLayout.setIsChildInGroup(childInGroup);
+        if (mNotificationInflater.setIsChildInGroup(childInGroup)) {
+            onNotificationUpdated();
+        }
         resetBackgroundAlpha();
         updateBackgroundForGroupState();
         updateClickAndFocus();
@@ -671,9 +687,13 @@
         return mPrivateLayout.getNotificationHeader();
     }
 
-    private NotificationHeaderView getVisibleNotificationHeader() {
+    /**
+     * @return the currently visible notification header. This can be different from
+     * {@link #getNotificationHeader()} in case it is a low-priority group.
+     */
+    public NotificationHeaderView getVisibleNotificationHeader() {
         if (mIsSummaryWithChildren && !mShowingPublic) {
-            return mChildrenContainer.getHeaderView();
+            return mChildrenContainer.getVisibleHeader();
         }
         return getShowingLayout().getVisibleNotificationHeader();
     }
@@ -711,7 +731,7 @@
         }
     }
 
-    public void reInflateViews() {
+    public void onDensityOrFontScaleChanged() {
         initDimens();
         if (mIsSummaryWithChildren) {
             if (mChildrenContainer != null) {
@@ -742,6 +762,8 @@
         for (NotificationContentView l : mLayouts) {
             l.reInflateViews();
         }
+        mNotificationInflater.onDensityOrFontScaleChanged();
+        onNotificationUpdated();
     }
 
     public void setContentBackground(int customBackgroundColor, boolean animate,
@@ -891,7 +913,7 @@
     }
 
     public View getNotificationIcon() {
-        NotificationHeaderView notificationHeader = getNotificationHeader();
+        NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
         if (notificationHeader != null) {
             return notificationHeader.getIcon();
         }
@@ -1008,17 +1030,32 @@
     public void setIsLowPriority(boolean isLowPriority) {
         mIsLowPriority = isLowPriority;
         mPrivateLayout.setIsLowPriority(isLowPriority);
+        mNotificationInflater.setIsLowPriority(mIsLowPriority);
         if (mChildrenContainer != null) {
             mChildrenContainer.setIsLowPriority(isLowPriority);
         }
     }
 
+    public boolean isLowPriority() {
+        return mIsLowPriority;
+    }
+
     public void setUseIncreasedCollapsedHeight(boolean use) {
         mUseIncreasedCollapsedHeight = use;
+        mNotificationInflater.setUsesIncreasedHeight(use);
     }
 
     public void setUseIncreasedHeadsUpHeight(boolean use) {
         mUseIncreasedHeadsUpHeight = use;
+        mNotificationInflater.setUsesIncreasedHeadsUpHeight(use);
+    }
+
+    public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
+        mNotificationInflater.setRemoteViewClickHandler(remoteViewClickHandler);
+    }
+
+    public void setInflateExceptionHandler(InflationExceptionHandler inflateExceptionHandler) {
+        mNotificationInflater.setInflateExceptionHandler(inflateExceptionHandler);
     }
 
     public interface ExpansionLogger {
@@ -1028,6 +1065,7 @@
     public ExpandableNotificationRow(Context context, AttributeSet attrs) {
         super(context, attrs);
         mFalsingManager = FalsingManager.getInstance(context);
+        mNotificationInflater = new NotificationInflater(this);
         initDimens();
     }
 
@@ -1063,26 +1101,8 @@
     /**
      * Resets this view so it can be re-used for an updated notification.
      */
-    @Override
     public void reset() {
-        super.reset();
-        final boolean wasExpanded = isExpanded();
-        mExpandable = false;
-        mHasUserChangedExpansion = false;
-        mUserLocked = false;
-        mShowingPublic = false;
-        mSensitive = false;
         mShowingPublicInitialized = false;
-        mIsSystemExpanded = false;
-        mOnKeyguard = false;
-        mPublicLayout.reset();
-        mPrivateLayout.reset();
-        resetHeight();
-        resetTranslation();
-        logExpansionEvent(false, wasExpanded);
-    }
-
-    public void resetHeight() {
         onHeightReset();
         requestLayout();
     }
@@ -1124,7 +1144,7 @@
             public void onInflate(ViewStub stub, View inflated) {
                 mChildrenContainer = (NotificationChildrenContainer) inflated;
                 mChildrenContainer.setIsLowPriority(mIsLowPriority);
-                mChildrenContainer.setNotificationParent(ExpandableNotificationRow.this);
+                mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
                 mChildrenContainer.onNotificationUpdated();
                 mTranslateableViews.add(mChildrenContainer);
             }
@@ -1253,9 +1273,7 @@
         if (mChildrenContainer != null) {
             mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
                     : INVISIBLE);
-            mChildrenContainer.updateHeaderVisibility(!mShowingPublic && mIsSummaryWithChildren
-                    ? VISIBLE
-                    : INVISIBLE);
+            mChildrenContainer.setHeaderVisible(!mShowingPublic && mIsSummaryWithChildren);
         }
         // The limits might have changed if the view suddenly became a group or vice versa
         updateLimits();
@@ -1323,7 +1341,6 @@
      */
     public void setUserExpanded(boolean userExpanded) {
         setUserExpanded(userExpanded, false /* allowChildExpansion */);
-        updateShelfIconColor();
     }
 
     /**
@@ -1334,22 +1351,28 @@
      */
     public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
         mFalsingManager.setNotificationExpanded();
-        if (mIsSummaryWithChildren && !mShowingPublic && allowChildExpansion) {
+        if (mIsSummaryWithChildren && !mShowingPublic && allowChildExpansion
+                && !mChildrenContainer.showingAsLowPriority()) {
             final boolean wasExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
             mGroupManager.setGroupExpanded(mStatusBarNotification, userExpanded);
-            logExpansionEvent(true /* userAction */, wasExpanded);
+            onExpansionChanged(true /* userAction */, wasExpanded);
             return;
         }
         if (userExpanded && !mExpandable) return;
         final boolean wasExpanded = isExpanded();
         mHasUserChangedExpansion = true;
         mUserExpanded = userExpanded;
-        logExpansionEvent(true, wasExpanded);
+        onExpansionChanged(true /* userAction */, wasExpanded);
     }
 
     public void resetUserExpansion() {
+        boolean changed = mUserExpanded;
         mHasUserChangedExpansion = false;
         mUserExpanded = false;
+        if (changed && mIsSummaryWithChildren) {
+            mChildrenContainer.onExpansionChanged();
+        }
+        updateShelfIconColor();
     }
 
     public boolean isUserLocked() {
@@ -1383,9 +1406,8 @@
         if (expand != mIsSystemExpanded) {
             final boolean wasExpanded = isExpanded();
             mIsSystemExpanded = expand;
-            updateShelfIconColor();
             notifyHeightChanged(false /* needsAnimation */);
-            logExpansionEvent(false, wasExpanded);
+            onExpansionChanged(false /* userAction */, wasExpanded);
             if (mIsSummaryWithChildren) {
                 mChildrenContainer.updateGroupOverflow();
             }
@@ -1399,7 +1421,7 @@
         if (onKeyguard != mOnKeyguard) {
             final boolean wasExpanded = isExpanded();
             mOnKeyguard = onKeyguard;
-            logExpansionEvent(false, wasExpanded);
+            onExpansionChanged(false /* userAction */, wasExpanded);
             if (wasExpanded != isExpanded()) {
                 if (mIsSummaryWithChildren) {
                     mChildrenContainer.updateGroupOverflow();
@@ -1470,8 +1492,8 @@
         mIsSummaryWithChildren = StatusBar.ENABLE_CHILD_NOTIFICATIONS
                 && mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0;
         if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
-            mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
-                    mEntry.notification);
+            mChildrenContainer.recreateNotificationHeader(mExpandClickListener
+            );
         }
         getShowingLayout().updateBackgroundColor(false /* animate */);
         mPrivateLayout.updateExpandButtons(isExpandable());
@@ -1526,7 +1548,7 @@
      * the top.
      */
     private void updateContentShiftHeight() {
-        NotificationHeaderView notificationHeader = getNotificationHeader();
+        NotificationHeaderView notificationHeader = getVisibleNotificationHeader();
         if (notificationHeader != null) {
             CachingIconView icon = notificationHeader.getIcon();
             mIconTransformContentShift = getRelativeTopPadding(icon) + icon.getHeight();
@@ -1737,7 +1759,17 @@
 
     @Override
     public void setActualHeight(int height, boolean notifyListeners) {
+        boolean changed = height != getActualHeight();
         super.setActualHeight(height, notifyListeners);
+        if (changed && isRemoved()) {
+            // TODO: remove this once we found the gfx bug for this.
+            // This is a hack since a removed view sometimes would just stay blank. it occured
+            // when sending yourself a message and then clicking on it.
+            ViewGroup parent = (ViewGroup) getParent();
+            if (parent != null) {
+                parent.invalidate();
+            }
+        }
         if (mGuts != null && mGuts.isExposed()) {
             mGuts.setActualHeight(height);
             return;
@@ -1828,9 +1860,9 @@
         return mShowingPublic ? mPublicLayout : mPrivateLayout;
     }
 
-    public void setShowingLegacyBackground(boolean showing) {
+    public void setLegacy(boolean legacy) {
         for (NotificationContentView l : mLayouts) {
-            l.setShowingLegacyBackground(showing);
+            l.setLegacy(legacy);
         }
     }
 
@@ -1910,7 +1942,7 @@
             if (isGroupExpanded()) {
                 return 1.0f;
             } else if (isUserLocked()) {
-                return mChildrenContainer.getGroupExpandFraction();
+                return mChildrenContainer.getIncreasedPaddingAmount();
             }
         } else if (isColorized() && (!mIsLowPriority || isExpanded())) {
             return -1.0f;
@@ -1933,13 +1965,19 @@
         return super.disallowSingleClick(event);
     }
 
-    private void logExpansionEvent(boolean userAction, boolean wasExpanded) {
+    private void onExpansionChanged(boolean userAction, boolean wasExpanded) {
         boolean nowExpanded = isExpanded();
-        if (mIsSummaryWithChildren) {
+        if (mIsSummaryWithChildren && (!mIsLowPriority || wasExpanded)) {
             nowExpanded = mGroupManager.isGroupExpanded(mStatusBarNotification);
         }
-        if (wasExpanded != nowExpanded && mLogger != null) {
-            mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded) ;
+        if (nowExpanded != wasExpanded) {
+            updateShelfIconColor();
+            if (mLogger != null) {
+                mLogger.logNotificationExpansion(mLoggingKey, userAction, nowExpanded);
+            }
+            if (mIsSummaryWithChildren) {
+                mChildrenContainer.onExpansionChanged();
+            }
         }
     }
 
@@ -1980,7 +2018,8 @@
 
     @Override
     public boolean isAboveShelf() {
-        return mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf);
+        return !isOnKeyguard()
+                && (mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf));
     }
 
     public void setShowAmbient(boolean showAmbient) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 57d2e1c..74e65fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -80,7 +80,7 @@
     private boolean mDark;
     private boolean mAnimate;
     private boolean mIsHeadsUp;
-    private boolean mShowingLegacyBackground;
+    private boolean mLegacy;
     private boolean mIsChildInGroup;
     private int mSmallHeight;
     private int mHeadsUpHeight;
@@ -140,7 +140,6 @@
                 R.dimen.min_notification_layout_height);
         mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_content_margin_end);
-        reset();
     }
 
     public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
@@ -329,41 +328,6 @@
         updateVisibility();
     }
 
-    public void reset() {
-        mPreviousExpandedRemoteInputIntent = null;
-        if (mExpandedRemoteInput != null) {
-            mExpandedRemoteInput.onNotificationUpdateOrReset();
-            if (mExpandedRemoteInput.isActive()) {
-                mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
-                mCachedExpandedRemoteInput = mExpandedRemoteInput;
-                mExpandedRemoteInput.dispatchStartTemporaryDetach();
-                ((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
-            }
-        }
-        if (mExpandedChild != null) {
-            mExpandedChild.animate().cancel();
-            removeView(mExpandedChild);
-            mExpandedRemoteInput = null;
-        }
-        mPreviousHeadsUpRemoteInputIntent = null;
-        if (mHeadsUpRemoteInput != null) {
-            mHeadsUpRemoteInput.onNotificationUpdateOrReset();
-            if (mHeadsUpRemoteInput.isActive()) {
-                mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
-                mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
-                mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
-                ((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
-            }
-        }
-        if (mHeadsUpChild != null) {
-            mHeadsUpChild.animate().cancel();
-            removeView(mHeadsUpChild);
-            mHeadsUpRemoteInput = null;
-        }
-        mExpandedChild = null;
-        mHeadsUpChild = null;
-    }
-
     public View getContractedChild() {
         return mContractedChild;
     }
@@ -394,8 +358,30 @@
 
     public void setExpandedChild(View child) {
         if (mExpandedChild != null) {
+            mPreviousExpandedRemoteInputIntent = null;
+            if (mExpandedRemoteInput != null) {
+                mExpandedRemoteInput.onNotificationUpdateOrReset();
+                if (mExpandedRemoteInput.isActive()) {
+                    mPreviousExpandedRemoteInputIntent = mExpandedRemoteInput.getPendingIntent();
+                    mCachedExpandedRemoteInput = mExpandedRemoteInput;
+                    mExpandedRemoteInput.dispatchStartTemporaryDetach();
+                    ((ViewGroup)mExpandedRemoteInput.getParent()).removeView(mExpandedRemoteInput);
+                }
+            }
             mExpandedChild.animate().cancel();
             removeView(mExpandedChild);
+            mExpandedRemoteInput = null;
+        }
+        if (child == null) {
+            mExpandedChild = null;
+            mExpandedWrapper = null;
+            if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
+                mVisibleType = VISIBLE_TYPE_CONTRACTED;
+            }
+            if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
+                mTransformationStartVisibleType = UNDEFINED;
+            }
+            return;
         }
         addView(child);
         mExpandedChild = child;
@@ -405,8 +391,30 @@
 
     public void setHeadsUpChild(View child) {
         if (mHeadsUpChild != null) {
+            mPreviousHeadsUpRemoteInputIntent = null;
+            if (mHeadsUpRemoteInput != null) {
+                mHeadsUpRemoteInput.onNotificationUpdateOrReset();
+                if (mHeadsUpRemoteInput.isActive()) {
+                    mPreviousHeadsUpRemoteInputIntent = mHeadsUpRemoteInput.getPendingIntent();
+                    mCachedHeadsUpRemoteInput = mHeadsUpRemoteInput;
+                    mHeadsUpRemoteInput.dispatchStartTemporaryDetach();
+                    ((ViewGroup)mHeadsUpRemoteInput.getParent()).removeView(mHeadsUpRemoteInput);
+                }
+            }
             mHeadsUpChild.animate().cancel();
             removeView(mHeadsUpChild);
+            mHeadsUpRemoteInput = null;
+        }
+        if (child == null) {
+            mHeadsUpChild = null;
+            mHeadsUpWrapper = null;
+            if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
+                mVisibleType = VISIBLE_TYPE_CONTRACTED;
+            }
+            if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
+                mTransformationStartVisibleType = UNDEFINED;
+            }
+            return;
         }
         addView(child);
         mHeadsUpChild = child;
@@ -623,7 +631,7 @@
     }
 
     public int getMinHeight(boolean likeGroupExpanded) {
-        if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded() || mIsLowPriority) {
+        if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
             return mContractedChild.getHeight();
         } else {
             return mSingleLineView.getHeight();
@@ -897,7 +905,7 @@
                 height = mContentHeight;
             }
             int expandedVisualType = getVisualTypeForHeight(height);
-            int collapsedVisualType = mIsChildInGroup && !isGroupExpanded() && !mIsLowPriority
+            int collapsedVisualType = mIsChildInGroup && !isGroupExpanded()
                     ? VISIBLE_TYPE_SINGLELINE
                     : getVisualTypeForHeight(mContainingNotification.getCollapsedHeight());
             return mTransformationStartVisibleType == collapsedVisualType
@@ -918,7 +926,7 @@
         if (!noExpandedChild && viewHeight == mExpandedChild.getHeight()) {
             return VISIBLE_TYPE_EXPANDED;
         }
-        if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded() && !mIsLowPriority) {
+        if (!mUserExpanding && mIsChildInGroup && !isGroupExpanded()) {
             return VISIBLE_TYPE_SINGLELINE;
         }
 
@@ -978,25 +986,37 @@
         return false;
     }
 
-    public void setShowingLegacyBackground(boolean showing) {
-        mShowingLegacyBackground = showing;
-        updateShowingLegacyBackground();
+    public void setLegacy(boolean legacy) {
+        mLegacy = legacy;
+        updateLegacy();
     }
 
-    private void updateShowingLegacyBackground() {
+    private void updateLegacy() {
         if (mContractedChild != null) {
-            mContractedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
+            mContractedWrapper.setLegacy(mLegacy);
         }
         if (mExpandedChild != null) {
-            mExpandedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
+            mExpandedWrapper.setLegacy(mLegacy);
         }
         if (mHeadsUpChild != null) {
-            mHeadsUpWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
+            mHeadsUpWrapper.setLegacy(mLegacy);
         }
     }
 
     public void setIsChildInGroup(boolean isChildInGroup) {
         mIsChildInGroup = isChildInGroup;
+        if (mContractedChild != null) {
+            mContractedWrapper.setIsChildInGroup(mIsChildInGroup);
+        }
+        if (mExpandedChild != null) {
+            mExpandedWrapper.setIsChildInGroup(mIsChildInGroup);
+        }
+        if (mHeadsUpChild != null) {
+            mHeadsUpWrapper.setIsChildInGroup(mIsChildInGroup);
+        }
+        if (mAmbientChild != null) {
+            mAmbientWrapper.setIsChildInGroup(mIsChildInGroup);
+        }
         updateSingleLineView();
     }
 
@@ -1005,19 +1025,19 @@
         mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
         updateSingleLineView();
         if (mContractedChild != null) {
-            mContractedWrapper.notifyContentUpdated(entry.notification, mIsLowPriority);
+            mContractedWrapper.notifyContentUpdated(entry.row);
         }
         if (mExpandedChild != null) {
-            mExpandedWrapper.notifyContentUpdated(entry.notification, mIsLowPriority);
+            mExpandedWrapper.notifyContentUpdated(entry.row);
         }
         if (mHeadsUpChild != null) {
-            mHeadsUpWrapper.notifyContentUpdated(entry.notification, mIsLowPriority);
+            mHeadsUpWrapper.notifyContentUpdated(entry.row);
         }
         if (mAmbientChild != null) {
-            mAmbientWrapper.notifyContentUpdated(entry.notification, mIsLowPriority);
+            mAmbientWrapper.notifyContentUpdated(entry.row);
         }
         applyRemoteInput(entry);
-        updateShowingLegacyBackground();
+        updateLegacy();
         mForceSelectNextLayout = true;
         setDark(mDark, false /* animate */, 0 /* delay */);
         mPreviousExpandedRemoteInputIntent = null;
@@ -1176,7 +1196,8 @@
         mExpandable = expandable;
         // if the expanded child has the same height as the collapsed one we hide it.
         if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
-            if (!mIsHeadsUp || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
+            if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
+                    || mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
                 if (mExpandedChild.getHeight() == mContractedChild.getHeight()) {
                     expandable = false;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 81db429..8f8d966 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -34,6 +34,7 @@
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.util.NotificationColorUtil;
+import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -65,7 +66,6 @@
         public ExpandableNotificationRow row; // the outer expanded view
         private boolean interruption;
         public boolean autoRedacted; // whether the redacted notification was generated by us
-        public boolean legacy; // whether the notification has a legacy, dark background
         public int targetSdk;
         private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
         public RemoteViews cachedContentView;
@@ -95,118 +95,20 @@
          * Resets the notification entry to be re-used.
          */
         public void reset() {
-            // NOTE: Icon needs to be preserved for now.
-            // We should fix this at some point.
-            autoRedacted = false;
-            legacy = false;
             lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
             if (row != null) {
                 row.reset();
             }
         }
 
-        public View getContentView() {
-            return row.getPrivateLayout().getContractedChild();
-        }
-
         public View getExpandedContentView() {
             return row.getPrivateLayout().getExpandedChild();
         }
 
-        public View getHeadsUpContentView() {
-            return row.getPrivateLayout().getHeadsUpChild();
-        }
-
         public View getPublicContentView() {
             return row.getPublicLayout().getContractedChild();
         }
 
-        public View getAmbientContentView() {
-            return row.getPrivateLayout().getAmbientChild();
-        }
-
-        public boolean cacheContentViews(Context ctx, Notification updatedNotification,
-                boolean isLowPriority, boolean useIncreasedCollapsedView,
-                boolean useIncreasedHeadsUp) {
-            boolean applyInPlace = false;
-            if (updatedNotification != null) {
-                final Notification.Builder updatedNotificationBuilder
-                        = Notification.Builder.recoverBuilder(ctx, updatedNotification);
-                final RemoteViews newContentView = createContentView(updatedNotificationBuilder,
-                        isLowPriority, useIncreasedCollapsedView);
-                final RemoteViews newBigContentView = createBigContentView(
-                        updatedNotificationBuilder, isLowPriority);
-                final RemoteViews newHeadsUpContentView =
-                        updatedNotificationBuilder.createHeadsUpContentView(useIncreasedHeadsUp);
-                final RemoteViews newPublicNotification
-                        = updatedNotificationBuilder.makePublicContentView();
-                final RemoteViews newAmbientNotification
-                        = updatedNotificationBuilder.makeAmbientNotification();
-
-                boolean sameCustomView = Objects.equals(
-                        notification.getNotification().extras.getBoolean(
-                                Notification.EXTRA_CONTAINS_CUSTOM_VIEW),
-                        updatedNotification.extras.getBoolean(
-                                Notification.EXTRA_CONTAINS_CUSTOM_VIEW));
-                applyInPlace = compareRemoteViews(cachedContentView, newContentView)
-                        && compareRemoteViews(cachedBigContentView, newBigContentView)
-                        && compareRemoteViews(cachedHeadsUpContentView, newHeadsUpContentView)
-                        && compareRemoteViews(cachedPublicContentView, newPublicNotification)
-                        && compareRemoteViews(cachedAmbientContentView, newAmbientNotification)
-                        && sameCustomView;
-                cachedPublicContentView = newPublicNotification;
-                cachedHeadsUpContentView = newHeadsUpContentView;
-                cachedBigContentView = newBigContentView;
-                cachedContentView = newContentView;
-                cachedAmbientContentView = newAmbientNotification;
-            } else {
-                final Notification.Builder builder
-                        = Notification.Builder.recoverBuilder(ctx, notification.getNotification());
-
-                cachedContentView = createContentView(builder, isLowPriority,
-                        useIncreasedCollapsedView);
-                cachedBigContentView = createBigContentView(builder, isLowPriority);
-                cachedHeadsUpContentView = builder.createHeadsUpContentView(useIncreasedHeadsUp);
-                cachedPublicContentView = builder.makePublicContentView();
-                cachedAmbientContentView = builder.makeAmbientNotification();
-
-                applyInPlace = false;
-            }
-            return applyInPlace;
-        }
-
-        private RemoteViews createBigContentView(Notification.Builder builder,
-                boolean isLowPriority) {
-            RemoteViews bigContentView = builder.createBigContentView();
-            if (bigContentView != null) {
-                return bigContentView;
-            }
-            if (isLowPriority) {
-                RemoteViews contentView = builder.createContentView();
-                Notification.Builder.makeHeaderExpanded(contentView);
-                return contentView;
-            }
-            return null;
-        }
-
-        private RemoteViews createContentView(Notification.Builder builder,
-                boolean isLowPriority, boolean useLarge) {
-            if (isLowPriority) {
-                return builder.makeLowPriorityContentView(false /* useRegularSubtext */);
-            }
-            return builder.createContentView(useLarge);
-        }
-
-        // Returns true if the RemoteViews are the same.
-        private boolean compareRemoteViews(final RemoteViews a, final RemoteViews b) {
-            return (a == null && b == null) ||
-                    (a != null && b != null
-                    && b.getPackage() != null
-                    && a.getPackage() != null
-                    && a.getPackage().equals(b.getPackage())
-                    && a.getLayoutId() == b.getLayoutId());
-        }
-
         public void notifyFullScreenIntentLaunched() {
             lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
         }
@@ -219,13 +121,14 @@
          * Create the icons for a notification
          * @param context the context to create the icons with
          * @param sbn the notification
-         * @throws IconException
+         * @throws InflationException
          */
-        public void createIcons(Context context, StatusBarNotification sbn) throws IconException {
+        public void createIcons(Context context, StatusBarNotification sbn)
+                throws InflationException {
             Notification n = sbn.getNotification();
             final Icon smallIcon = n.getSmallIcon();
             if (smallIcon == null) {
-                throw new IconException("No small icon in notification from "
+                throw new InflationException("No small icon in notification from "
                         + sbn.getPackageName());
             }
 
@@ -248,7 +151,7 @@
             if (!icon.set(ic) || !expandedIcon.set(ic)) {
                 icon = null;
                 expandedIcon = null;
-                throw new IconException("Couldn't create icon: " + ic);
+                throw new InflationException("Couldn't create icon: " + ic);
             }
             expandedIcon.setVisibility(View.INVISIBLE);
             expandedIcon.setOnVisibilityChangedListener(
@@ -270,9 +173,9 @@
          * Update the notification icons.
          * @param context the context to create the icons with.
          * @param n the notification to read the icon from.
-         * @throws IconException
+         * @throws InflationException
          */
-        public void updateIcons(Context context, Notification n) throws IconException {
+        public void updateIcons(Context context, Notification n) throws InflationException {
             if (icon != null) {
                 // Update the icon
                 final StatusBarIcon ic = new StatusBarIcon(
@@ -285,7 +188,7 @@
                 icon.setNotification(n);
                 expandedIcon.setNotification(n);
                 if (!icon.set(ic) || !expandedIcon.set(ic)) {
-                    throw new IconException("Couldn't update icon: " + ic);
+                    throw new InflationException("Couldn't update icon: " + ic);
                 }
             }
         }
@@ -612,10 +515,4 @@
         public String getCurrentMediaNotificationKey();
         public NotificationGroupManager getGroupManager();
     }
-
-    public static class IconException extends Exception {
-        IconException(String error) {
-            super(error);
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 9703235..af464c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -72,11 +72,6 @@
     private StatusBarNotification mStatusBarNotification;
     private NotificationChannel mNotificationChannel;
 
-    private ImageView mAutoButton;
-    private TextView mImportanceSummary;
-    private TextView mImportanceTitle;
-    private boolean mAuto;
-
     private TextView mNumChannelsView;
     private View mChannelDisabledView;
     private Switch mChannelEnabledSwitch;
@@ -105,8 +100,10 @@
         int appUid = -1;
         String appName = pkg;
         Drawable pkgicon = null;
+        CharSequence channelNameText = "";
+        ApplicationInfo info = null;
         try {
-            final ApplicationInfo info = pm.getApplicationInfo(pkg,
+            info = pm.getApplicationInfo(pkg,
                     PackageManager.MATCH_UNINSTALLED_PACKAGES
                             | PackageManager.MATCH_DISABLED_COMPONENTS
                             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
@@ -115,6 +112,7 @@
                 appUid = info.uid;
                 appName = String.valueOf(pm.getApplicationLabel(info));
                 pkgicon = pm.getApplicationIcon(info);
+
             }
         } catch (PackageManager.NameNotFoundException e) {
             // app is gone, just show package name and generic icon
@@ -135,11 +133,15 @@
                 R.plurals.notification_num_channels_desc, numChannels), numChannels));
 
         // If this is the placeholder channel, don't use our channel-specific text.
-        CharSequence channelNameText;
         if (channel.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
             channelNameText = mContext.getString(R.string.notification_header_default_channel);
         } else {
-            channelNameText = channel.getName();
+            if (info != null && channel.getNameResId() != 0) {
+                channelNameText = pm.getText(pkg, channel.getNameResId(), info);
+            }
+            if (channel.getName() != null) {
+                channelNameText = channel.getName();
+            }
         }
         ((TextView) findViewById(R.id.pkgname)).setText(appName);
         ((TextView) findViewById(R.id.channel_name)).setText(channelNameText);
@@ -152,7 +154,12 @@
                         iNotificationManager.getNotificationChannelGroupForPackage(
                                 channel.getGroup(), pkg, appUid);
                 if (notificationChannelGroup != null) {
-                    groupName = notificationChannelGroup.getName();
+                    if (info != null && notificationChannelGroup.getNameResId() != 0) {
+                        groupName = pm.getText(pkg, notificationChannelGroup.getNameResId(), info);
+                    }
+                    if (notificationChannelGroup.getName() != null) {
+                        groupName = notificationChannelGroup.getName();
+                    }
                 }
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
@@ -171,8 +178,8 @@
 
         boolean nonBlockable = false;
         try {
-            final PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
-            nonBlockable = Utils.isSystemPackage(getResources(), pm, info);
+            final PackageInfo pkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
+            nonBlockable = Utils.isSystemPackage(getResources(), pm, pkgInfo);
         } catch (PackageManager.NameNotFoundException e) {
             // unlikely.
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 1cd909ea..8da17fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -41,7 +41,8 @@
  * A notification shelf view that is placed inside the notification scroller. It manages the
  * overflow icons that don't fit into the regular list anymore.
  */
-public class NotificationShelf extends ActivatableNotificationView {
+public class NotificationShelf extends ActivatableNotificationView implements
+        View.OnLayoutChangeListener {
 
     public static final boolean SHOW_AMBIENT_ICONS = true;
     private static final boolean USE_ANIMATIONS_WHEN_OPENING =
@@ -443,7 +444,8 @@
             }
             int shelfColor = icon.getStaticDrawableColor();
             if (!noIcon && shelfColor != StatusBarIconView.NO_COLOR) {
-                int notificationColor = row.getNotificationHeader().getOriginalNotificationColor();
+                int notificationColor
+                        = row.getVisibleNotificationHeader().getOriginalNotificationColor();
                 shelfColor = NotificationUtils.interpolateColors(notificationColor, shelfColor,
                         iconState.iconAppearAmount);
             }
@@ -493,6 +495,10 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
+        updateRelativeOffset();
+    }
+
+    private void updateRelativeOffset() {
         mCollapsedIcons.getLocationOnScreen(mTmp);
         mRelativeOffset = mTmp[0];
         getLocationOnScreen(mTmp);
@@ -559,6 +565,7 @@
 
     public void setCollapsedIcons(NotificationIconContainer collapsedIcons) {
         mCollapsedIcons = collapsedIcons;
+        mCollapsedIcons.addOnLayoutChangeListener(this);
     }
 
     public void setStatusBarState(int statusBarState) {
@@ -594,6 +601,12 @@
         }
     }
 
+    @Override
+    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
+            int oldTop, int oldRight, int oldBottom) {
+        updateRelativeOffset();
+    }
+
     private class ShelfState extends ExpandableViewState {
         private float openedAmount;
         private boolean hasItemsInStableShelf;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index aec9a4b..1101701 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -612,8 +612,10 @@
     }
 
     public void setIconAppearAmount(float iconAppearAmount) {
-        mIconAppearAmount = iconAppearAmount;
-        invalidate();
+        if (mIconAppearAmount != iconAppearAmount) {
+            mIconAppearAmount = iconAppearAmount;
+            invalidate();
+        }
     }
 
     public float getIconAppearAmount() {
@@ -625,8 +627,10 @@
     }
 
     public void setDotAppearAmount(float dotAppearAmount) {
-        mDotAppearAmount = dotAppearAmount;
-        invalidate();
+        if (mDotAppearAmount != dotAppearAmount) {
+            mDotAppearAmount = dotAppearAmount;
+            invalidate();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InflationException.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InflationException.java
new file mode 100644
index 0000000..b484138
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InflationException.java
@@ -0,0 +1,26 @@
+/*
+ * 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.notification;
+
+/**
+ * An exception that something went wrong during the inflation
+ */
+public class InflationException extends Exception {
+    public InflationException(String error) {
+        super(error);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java
index 78b967a..7a34b6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigPictureTemplateViewWrapper.java
@@ -36,9 +36,9 @@
     }
 
     @Override
-    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
-        super.notifyContentUpdated(notification, isLowPriority);
-        updateImageTag(notification);
+    public void notifyContentUpdated(ExpandableNotificationRow row) {
+        super.notifyContentUpdated(row);
+        updateImageTag(row.getStatusBarNotification());
     }
 
     private void updateImageTag(StatusBarNotification notification) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java
index 39db243..9476eed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationBigTextTemplateViewWrapper.java
@@ -41,11 +41,11 @@
     }
 
     @Override
-    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
+    public void notifyContentUpdated(ExpandableNotificationRow row) {
         // Reinspect the notification. Before the super call, because the super call also updates
         // the transformation types and we need to have our values set by then.
-        resolveViews(notification);
-        super.notifyContentUpdated(notification, isLowPriority);
+        resolveViews(row.getStatusBarNotification());
+        super.notifyContentUpdated(row);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index e5f32df..3efa29f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -19,13 +19,8 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
-import android.graphics.Color;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.service.notification.StatusBarNotification;
-import android.support.v4.graphics.ColorUtils;
 import android.view.View;
 
 import com.android.systemui.R;
@@ -40,7 +35,7 @@
 
     private final ViewInvertHelper mInvertHelper;
     private final Paint mGreyPaint = new Paint();
-    private boolean mShowingLegacyBackground;
+    private boolean mIsLegacy;
     private int mLegacyColor;
 
     protected NotificationCustomViewWrapper(View view, ExpandableNotificationRow row) {
@@ -55,7 +50,7 @@
             return;
         }
         super.setDark(dark, fade, delay);
-        if (!mShowingLegacyBackground && mShouldInvertDark) {
+        if (!mIsLegacy && mShouldInvertDark) {
             if (fade) {
                 mInvertHelper.fade(dark, delay);
             } else {
@@ -112,15 +107,14 @@
     @Override
     public int getCustomBackgroundColor() {
         int customBackgroundColor = super.getCustomBackgroundColor();
-        if (customBackgroundColor == 0 && mShowingLegacyBackground) {
+        if (customBackgroundColor == 0 && mIsLegacy) {
             return mLegacyColor;
         }
         return customBackgroundColor;
     }
 
-    @Override
-    public void setShowingLegacyBackground(boolean showing) {
-        super.setShowingLegacyBackground(showing);
-        mShowingLegacyBackground = showing;
+    public void setLegacy(boolean legacy) {
+        super.setLegacy(legacy);
+        mIsLegacy = legacy;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 8eab2e9..38e4ec1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.app.Notification;
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
@@ -26,7 +27,6 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
-import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
 import android.view.NotificationHeaderView;
 import android.view.View;
@@ -71,6 +71,7 @@
     private TextView mHeaderText;
     private ImageView mWorkProfileImage;
     private boolean mIsLowPriority;
+    private boolean mTransformLowPriorityTitle;
 
     protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
         super(view, row);
@@ -100,7 +101,7 @@
 
                     @Override
                     protected boolean hasCustomTransformation() {
-                        return mIsLowPriority;
+                        return mIsLowPriority && mTransformLowPriorityTitle;
                     }
                 }, TRANSFORMING_VIEW_TITLE);
         resolveHeaderViews();
@@ -128,9 +129,10 @@
     }
 
     @Override
-    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
-        super.notifyContentUpdated(notification, isLowPriority);
-        mIsLowPriority = isLowPriority;
+    public void notifyContentUpdated(ExpandableNotificationRow row) {
+        super.notifyContentUpdated(row);
+        mIsLowPriority = row.isLowPriority();
+        mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren();
         ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews();
 
         // Reinspect the notification.
@@ -139,11 +141,11 @@
         updateTransformedTypes();
         addRemainingTransformTypes();
         updateCropToPaddingForImageViews();
-        mIcon.setTag(ImageTransformState.ICON_TAG, notification.getNotification().getSmallIcon());
+        Notification notification = row.getStatusBarNotification().getNotification();
+        mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
         // The work profile image is always the same lets just set the icon tag for it not to
         // animate
-        mWorkProfileImage.setTag(ImageTransformState.ICON_TAG,
-                notification.getNotification().getSmallIcon());
+        mWorkProfileImage.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
 
         // We need to reset all views that are no longer transforming in case a view was previously
         // transformed, but now we decided to transform its container instead.
@@ -361,6 +363,12 @@
     }
 
     @Override
+    public void setIsChildInGroup(boolean isChildInGroup) {
+        super.setIsChildInGroup(isChildInGroup);
+        mTransformLowPriorityTitle = !isChildInGroup;
+    }
+
+    @Override
     public void setVisible(boolean visible) {
         super.setVisible(visible);
         mTransformationHelper.setVisible(visible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
new file mode 100644
index 0000000..66703ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
@@ -0,0 +1,278 @@
+/*
+ * 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.notification;
+
+import android.app.Notification;
+import android.content.Context;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.view.View;
+import android.widget.RemoteViews;
+
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationContentView;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import java.util.Objects;
+
+/**
+ * A utility that inflates the right kind of contentView based on the state
+ */
+public class NotificationInflater {
+
+    private static final int FLAG_REINFLATE_ALL = ~0;
+    private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0;
+    private static final int FLAG_REINFLATE_EXPANDED_VIEW = 1<<1;
+    private static final int FLAG_REINFLATE_HEADS_UP_VIEW = 1<<2;
+    private static final int FLAG_REINFLATE_PUBLIC_VIEW = 1<<3;
+    private static final int FLAG_REINFLATE_AMBIENT_VIEW = 1<<4;
+
+    private final ExpandableNotificationRow mRow;
+    private boolean mIsLowPriority;
+    private boolean mUsesIncreasedHeight;
+    private boolean mUsesIncreasedHeadsUpHeight;
+    private RemoteViews.OnClickHandler mRemoteViewClickHandler;
+    private boolean mIsChildInGroup;
+    private InflationExceptionHandler mInflateExceptionHandler;
+
+    public NotificationInflater(ExpandableNotificationRow row) {
+        mRow = row;
+    }
+
+    public void setIsLowPriority(boolean isLowPriority) {
+        mIsLowPriority = isLowPriority;
+    }
+
+    /**
+     * Set whether the notification is a child in a group
+     *
+     * @return whether the view was re-inflated
+     */
+    public boolean setIsChildInGroup(boolean childInGroup) {
+        if (childInGroup != mIsChildInGroup) {
+            mIsChildInGroup = childInGroup;
+            if (mIsLowPriority) {
+                try {
+                    int flags = FLAG_REINFLATE_CONTENT_VIEW | FLAG_REINFLATE_EXPANDED_VIEW;
+                    inflateNotificationViews(flags);
+                } catch (InflationException e) {
+                    mInflateExceptionHandler.handleInflationException(
+                            mRow.getStatusBarNotification(), e);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public void setUsesIncreasedHeight(boolean usesIncreasedHeight) {
+        mUsesIncreasedHeight = usesIncreasedHeight;
+    }
+
+    public void setUsesIncreasedHeadsUpHeight(boolean usesIncreasedHeight) {
+        mUsesIncreasedHeadsUpHeight = usesIncreasedHeight;
+    }
+
+    public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
+        mRemoteViewClickHandler = remoteViewClickHandler;
+    }
+
+    public void inflateNotificationViews() throws InflationException {
+        inflateNotificationViews(FLAG_REINFLATE_ALL);
+    }
+
+    /**
+     * reinflate all views for the specified flags
+     * @param reInflateFlags flags which views should be reinflated. Use {@link #FLAG_REINFLATE_ALL}
+     *                       to reinflate all of views.
+     * @throws InflationException
+     */
+    private void inflateNotificationViews(int reInflateFlags)
+            throws InflationException {
+        NotificationData.Entry entry = mRow.getEntry();
+        StatusBarNotification sbn = entry.notification;
+        Context context = mRow.getContext();
+        NotificationContentView privateLayout = mRow.getPrivateLayout();
+        try {
+            final Notification.Builder recoveredBuilder
+                    = Notification.Builder.recoverBuilder(context, sbn.getNotification());
+            boolean isLowPriority = mIsLowPriority && !mIsChildInGroup;
+            if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
+                final RemoteViews newContentView = createContentView(recoveredBuilder,
+                        isLowPriority, mUsesIncreasedHeadsUpHeight);
+                if (!compareRemoteViews(newContentView,
+                        entry.cachedContentView)) {
+                    View contentViewLocal = newContentView.apply(
+                            sbn.getPackageContext(context),
+                            privateLayout,
+                            mRemoteViewClickHandler);
+                    contentViewLocal.setIsRootNamespace(true);
+                    privateLayout.setContractedChild(contentViewLocal);
+                } else {
+                    newContentView.reapply(sbn.getPackageContext(context),
+                            privateLayout.getContractedChild(),
+                            mRemoteViewClickHandler);
+                }
+                entry.cachedContentView = newContentView;
+            }
+
+            if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
+                final RemoteViews newBigContentView = createBigContentView(
+                        recoveredBuilder, isLowPriority);
+                if (newBigContentView != null) {
+                    if (!compareRemoteViews(newBigContentView, entry.cachedBigContentView)) {
+                        View bigContentViewLocal = newBigContentView.apply(
+                                sbn.getPackageContext(context),
+                                privateLayout,
+                                mRemoteViewClickHandler);
+                        bigContentViewLocal.setIsRootNamespace(true);
+                        privateLayout.setExpandedChild(bigContentViewLocal);
+                    } else {
+                        newBigContentView.reapply(sbn.getPackageContext(context),
+                                privateLayout.getExpandedChild(),
+                                mRemoteViewClickHandler);
+                    }
+                } else if (entry.cachedBigContentView != null) {
+                    privateLayout.setExpandedChild(null);
+                }
+                entry.cachedBigContentView = newBigContentView;
+                mRow.setExpandable(newBigContentView != null);
+            }
+
+            if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
+                final RemoteViews newHeadsUpContentView =
+                        recoveredBuilder.createHeadsUpContentView(mUsesIncreasedHeight);
+                if (newHeadsUpContentView != null) {
+                    if (!compareRemoteViews(newHeadsUpContentView,
+                            entry.cachedHeadsUpContentView)) {
+                        View headsUpContentViewLocal = newHeadsUpContentView.apply(
+                                sbn.getPackageContext(context),
+                                privateLayout,
+                                mRemoteViewClickHandler);
+                        headsUpContentViewLocal.setIsRootNamespace(true);
+                        privateLayout.setHeadsUpChild(headsUpContentViewLocal);
+                    } else {
+                        newHeadsUpContentView.reapply(sbn.getPackageContext(context),
+                                privateLayout.getHeadsUpChild(),
+                                mRemoteViewClickHandler);
+                    }
+                } else if (entry.cachedHeadsUpContentView != null) {
+                    privateLayout.setHeadsUpChild(null);
+                }
+                entry.cachedHeadsUpContentView = newHeadsUpContentView;
+            }
+
+            if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
+                NotificationContentView publicLayout = mRow.getPublicLayout();
+                final RemoteViews newPublicNotification
+                        = recoveredBuilder.makePublicContentView();
+                if (!compareRemoteViews(newPublicNotification, entry.cachedPublicContentView)) {
+                    View publicContentView = newPublicNotification.apply(
+                            sbn.getPackageContext(context),
+                            publicLayout,
+                            mRemoteViewClickHandler);
+                    publicContentView.setIsRootNamespace(true);
+                    publicLayout.setContractedChild(publicContentView);
+                } else {
+                    newPublicNotification.reapply(sbn.getPackageContext(context),
+                            publicLayout.getContractedChild(),
+                            mRemoteViewClickHandler);
+                }
+                entry.cachedPublicContentView = newPublicNotification;
+            }
+
+            if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
+                final RemoteViews newAmbientNotification
+                        = recoveredBuilder.makeAmbientNotification();
+                if (!compareRemoteViews(newAmbientNotification, entry.cachedAmbientContentView)) {
+                    View ambientContentView = newAmbientNotification.apply(
+                            sbn.getPackageContext(context),
+                            privateLayout,
+                            mRemoteViewClickHandler);
+                    ambientContentView.setIsRootNamespace(true);
+                    privateLayout.setAmbientChild(ambientContentView);
+                } else {
+                    newAmbientNotification.reapply(sbn.getPackageContext(context),
+                            privateLayout.getAmbientChild(),
+                            mRemoteViewClickHandler);
+                }
+                entry.cachedAmbientContentView = newAmbientNotification;
+            }
+
+        } catch (RuntimeException e) {
+            final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
+            Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
+            throw new InflationException("Couldn't inflate contentViews");
+        }
+    }
+
+    private RemoteViews createBigContentView(Notification.Builder builder,
+            boolean isLowPriority) {
+        RemoteViews bigContentView = builder.createBigContentView();
+        if (bigContentView != null) {
+            return bigContentView;
+        }
+        if (isLowPriority) {
+            RemoteViews contentView = builder.createContentView();
+            Notification.Builder.makeHeaderExpanded(contentView);
+            return contentView;
+        }
+        return null;
+    }
+
+    private RemoteViews createContentView(Notification.Builder builder,
+            boolean isLowPriority, boolean useLarge) {
+        if (isLowPriority) {
+            return builder.makeLowPriorityContentView(false /* useRegularSubtext */);
+        }
+        return builder.createContentView(useLarge);
+    }
+
+    // Returns true if the RemoteViews are the same.
+    private boolean compareRemoteViews(final RemoteViews a, final RemoteViews b) {
+        return (a == null && b == null) ||
+                (a != null && b != null
+                        && b.getPackage() != null
+                        && a.getPackage() != null
+                        && a.getPackage().equals(b.getPackage())
+                        && a.getLayoutId() == b.getLayoutId());
+    }
+
+    public void setInflateExceptionHandler(InflationExceptionHandler inflateExceptionHandler) {
+        mInflateExceptionHandler = inflateExceptionHandler;
+    }
+
+    public interface InflationExceptionHandler {
+        void handleInflationException(StatusBarNotification notification, InflationException e);
+    }
+    public void onDensityOrFontScaleChanged() {
+        NotificationData.Entry entry = mRow.getEntry();
+        entry.cachedAmbientContentView = null;
+        entry.cachedBigContentView = null;
+        entry.cachedContentView = null;
+        entry.cachedHeadsUpContentView = null;
+        entry.cachedPublicContentView = null;
+        try {
+            inflateNotificationViews();
+        } catch (InflationException e) {
+            mInflateExceptionHandler.handleInflationException(
+                    mRow.getStatusBarNotification(), e);
+        }
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
index 04ee6aa..ef5a25c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java
@@ -35,16 +35,16 @@
 
     View mActions;
 
-    private void resolveViews(StatusBarNotification notification) {
+    private void resolveViews() {
         mActions = mView.findViewById(com.android.internal.R.id.media_actions);
     }
 
     @Override
-    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
+    public void notifyContentUpdated(ExpandableNotificationRow row) {
         // Reinspect the notification. Before the super call, because the super call also updates
         // the transformation types and we need to have our values set by then.
-        resolveViews(notification);
-        super.notifyContentUpdated(notification, isLowPriority);
+        resolveViews();
+        super.notifyContentUpdated(row);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java
index defeab2..9631556 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMessagingTemplateViewWrapper.java
@@ -21,7 +21,6 @@
 import com.android.systemui.statusbar.TransformableView;
 
 import android.content.Context;
-import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.TextView;
@@ -71,11 +70,11 @@
     }
 
     @Override
-    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
+    public void notifyContentUpdated(ExpandableNotificationRow row) {
         // Reinspect the notification. Before the super call, because the super call also updates
         // the transformation types and we need to have our values set by then.
         resolveViews();
-        super.notifyContentUpdated(notification, isLowPriority);
+        super.notifyContentUpdated(row);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index e9956ff..846d03a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -133,11 +133,11 @@
     }
 
     @Override
-    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
+    public void notifyContentUpdated(ExpandableNotificationRow row) {
         // Reinspect the notification. Before the super call, because the super call also updates
         // the transformation types and we need to have our values set by then.
-        resolveTemplateViews(notification);
-        super.notifyContentUpdated(notification, isLowPriority);
+        resolveTemplateViews(row.getStatusBarNotification());
+        super.notifyContentUpdated(row);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 5f5e1e4..c85e8d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -48,6 +48,7 @@
     private int mBackgroundColor = 0;
     protected boolean mShouldInvertDark;
     protected boolean mDarkInitialized = false;
+    private boolean mForcedInvisible;
 
     public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) {
         if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
@@ -87,10 +88,9 @@
 
     /**
      * Notifies this wrapper that the content of the view might have changed.
-     * @param notification the notification this is wrapped around
-     * @param isLowPriority is this notification low priority
+     * @param row the row this wrapper is attached to
      */
-    public void notifyContentUpdated(StatusBarNotification notification, boolean isLowPriority) {
+    public void notifyContentUpdated(ExpandableNotificationRow row) {
         mDarkInitialized = false;
         Drawable background = mView.getBackground();
         if (shouldClearBackgroundOnReapply()) {
@@ -185,7 +185,7 @@
         return mRow.isSummaryWithChildren() ? 0 : mBackgroundColor;
     }
 
-    public void setShowingLegacyBackground(boolean showing) {
+    public void setLegacy(boolean legacy) {
     }
 
     public void setContentHeight(int contentHeight, int minHeightHint) {
@@ -193,4 +193,7 @@
 
     public void setRemoteInputVisible(boolean visible) {
     }
+
+    public void setIsChildInGroup(boolean isChildInGroup) {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 1f56c56..6cd3eae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -153,7 +153,7 @@
     }
 
     private boolean shouldHideNotificationIcons() {
-        return !mStatusBar.isClosed() && mStatusBarComponent.shouldHideNotificationIcons();
+        return !mStatusBar.isClosed() && mStatusBarComponent.hideStatusBarIconsWhenExpanded();
     }
 
     public void hideSystemIconArea(boolean animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 707997d..4581204 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -79,7 +79,9 @@
         for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
             View child = mNotificationIcons.getChildAt(i);
             child.setLayoutParams(params);
-            child = mShelfIcons.getChildAt(i);
+        }
+        for (int i = 0; i < mShelfIcons.getChildCount(); i++) {
+            View child = mShelfIcons.getChildAt(i);
             child.setLayoutParams(params);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index dc5f98c..3706dc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -227,6 +227,7 @@
             ViewState iconState = mIconStates.get(view);
             iconState.initFrom(view);
             iconState.alpha = 1.0f;
+            iconState.hidden = false;
         }
     }
 
@@ -524,11 +525,5 @@
                 iconColor = ((StatusBarIconView) view).getStaticDrawableColor();
             }
         }
-
-        protected void onYTranslationAnimationFinished(View view) {
-            if (hidden) {
-                view.setVisibility(INVISIBLE);
-            }
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 468fb57..e6d3168 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -26,7 +26,6 @@
 import android.content.Context;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
@@ -47,7 +46,6 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.DejankUtils;
-import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -179,7 +177,7 @@
     private boolean mKeyguardStatusViewAnimating;
     private ValueAnimator mQsSizeChangeAnimator;
 
-    private boolean mShadeEmpty;
+    private boolean mShowEmptyShadeView;
 
     private boolean mQsScrimEnabled = true;
     private boolean mLastAnnouncementWasQuickSettings;
@@ -211,11 +209,12 @@
         }
     };
     private NotificationGroupManager mGroupManager;
-    private boolean mOpening;
+    private boolean mShowIconsWhenExpanded;
     private int mIndicationBottomPadding;
     private boolean mIsFullWidth;
     private boolean mDark;
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
+    private boolean mNoVisibleNotifications = true;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -1518,7 +1517,7 @@
         // it in expanded QS state as well so we don't run into troubles when fading the view in/out
         // and expanding/collapsing the whole panel from/to quick settings.
         if (mNotificationStackScroller.getNotGoneChildCount() == 0
-                && mShadeEmpty) {
+                && mShowEmptyShadeView) {
             notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight();
         }
         int maxQsHeight = mQsMaxExpansionHeight;
@@ -2118,15 +2117,15 @@
         return mDozing;
     }
 
-    public void setShadeEmpty(boolean shadeEmpty) {
-        mShadeEmpty = shadeEmpty;
+    public void showEmptyShadeView(boolean emptyShadeViewVisible) {
+        mShowEmptyShadeView = emptyShadeViewVisible;
         updateEmptyShadeView();
     }
 
     private void updateEmptyShadeView() {
 
         // Hide "No notifications" in QS.
-        mNotificationStackScroller.updateEmptyShadeView(mShadeEmpty && !mQsExpanded);
+        mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded);
     }
 
     public void setQsScrimEnabled(boolean qsScrimEnabled) {
@@ -2306,7 +2305,7 @@
         }
         mNotificationStackScroller.setExpandedHeight(expandedHeight);
         updateKeyguardBottomAreaAlpha();
-        setOpening(isFullWidth() && expandedHeight < getOpeningHeight());
+        updateStatusBarIcons();
     }
 
     /**
@@ -2317,13 +2316,21 @@
         return mIsFullWidth;
     }
 
-    private void setOpening(boolean opening) {
-        if (opening != mOpening) {
-            mOpening = opening;
+    private void updateStatusBarIcons() {
+        boolean showIconsWhenExpanded = isFullWidth() && getExpandedHeight() < getOpeningHeight();
+        if (showIconsWhenExpanded && mNoVisibleNotifications && isOnKeyguard()) {
+            showIconsWhenExpanded = false;
+        }
+        if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
+            mShowIconsWhenExpanded = showIconsWhenExpanded;
             mStatusBar.recomputeDisableFlags(false);
         }
     }
 
+    private boolean isOnKeyguard() {
+        return mStatusBar.getBarState() == StatusBarState.KEYGUARD;
+    }
+
     public void setPanelScrimMinFraction(float minFraction) {
         mBar.panelScrimMinFractionChanged(minFraction);
     }
@@ -2426,12 +2433,8 @@
         mGroupManager = groupManager;
     }
 
-    public boolean shouldHideNotificationIcons() {
-        return !isFullWidth() || (!mOpening && !isFullyCollapsed());
-    }
-
-    public boolean shouldAnimateIconHiding() {
-        return !isFullWidth();
+    public boolean hideStatusBarIconsWhenExpanded() {
+        return !isFullWidth() || !mShowIconsWhenExpanded;
     }
 
     private final FragmentListener mFragmentListener = new FragmentListener() {
@@ -2473,4 +2476,8 @@
         mKeyguardStatusView.setDark(dark);
         positionClockAndNotifications();
     }
+
+    public void setNoVisibleNotifications(boolean noNotifications) {
+        mNoVisibleNotifications = noNotifications;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 9e93ed3..bb6c8f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.phone;
 
 import android.app.ActivityManager;
-import android.app.ActivityManager.RunningAppProcessInfo;
 import android.app.ActivityManager.StackId;
 import android.app.ActivityManager.StackInfo;
 import android.app.AlarmManager;
@@ -25,8 +24,6 @@
 import android.app.AppGlobals;
 import android.app.Notification;
 import android.app.Notification.Action;
-import android.app.Notification.BigTextStyle;
-import android.app.Notification.Style;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.SynchronousUserSwitchObserver;
@@ -37,7 +34,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.graphics.drawable.Icon;
 import android.media.AudioManager;
@@ -51,7 +47,6 @@
 import android.provider.Settings.Global;
 import android.service.notification.StatusBarNotification;
 import android.telecom.TelecomManager;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
@@ -61,11 +56,9 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.R.string;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
-import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
 import com.android.systemui.statusbar.CommandQueue;
@@ -87,9 +80,6 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.NotificationChannels;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * This class contains all of the policy about which icons are installed in the status
  * bar at boot time.  It goes through the normal API for icons, even though it probably
@@ -137,8 +127,6 @@
     private boolean mVolumeVisible;
     private boolean mCurrentUserSetup;
 
-    private int mZen;
-
     private boolean mManagedProfileFocused = false;
     private boolean mManagedProfileIconVisible = false;
     private boolean mManagedProfileInQuietMode = false;
@@ -275,14 +263,14 @@
 
     @Override
     public void onZenChanged(int zen) {
-        mZen = zen;
         updateVolumeZen();
     }
 
     private void updateAlarm() {
         final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
         final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
-        final boolean zenNone = mZen == Global.ZEN_MODE_NO_INTERRUPTIONS;
+        int zen = mZenController.getZen();
+        final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
         mIconController.setIcon(mSlotAlarmClock, zenNone ? R.drawable.stat_sys_alarm_dim
                 : R.drawable.stat_sys_alarm, null);
         mIconController.setIconVisibility(mSlotAlarmClock, mCurrentUserSetup && hasAlarm);
@@ -323,17 +311,18 @@
         boolean volumeVisible = false;
         int volumeIconId = 0;
         String volumeDescription = null;
+        int zen = mZenController.getZen();
 
         if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) {
-            zenVisible = mZen != Global.ZEN_MODE_OFF;
-            zenIconId = mZen == Global.ZEN_MODE_NO_INTERRUPTIONS
+            zenVisible = zen != Global.ZEN_MODE_OFF;
+            zenIconId = zen == Global.ZEN_MODE_NO_INTERRUPTIONS
                     ? R.drawable.stat_sys_dnd_total_silence : R.drawable.stat_sys_dnd;
             zenDescription = mContext.getString(R.string.quick_settings_dnd_label);
-        } else if (mZen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
+        } else if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
             zenVisible = true;
             zenIconId = R.drawable.stat_sys_zen_none;
             zenDescription = mContext.getString(R.string.interruption_level_none);
-        } else if (mZen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
+        } else if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
             zenVisible = true;
             zenIconId = R.drawable.stat_sys_zen_important;
             zenDescription = mContext.getString(R.string.interruption_level_priority);
@@ -344,7 +333,7 @@
             volumeVisible = true;
             volumeIconId = R.drawable.stat_sys_ringer_silent;
             volumeDescription = mContext.getString(R.string.accessibility_ringer_silent);
-        } else if (mZen != Global.ZEN_MODE_NO_INTERRUPTIONS && mZen != Global.ZEN_MODE_ALARMS &&
+        } else if (zen != Global.ZEN_MODE_NO_INTERRUPTIONS && zen != Global.ZEN_MODE_ALARMS &&
                 audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
             volumeVisible = true;
             volumeIconId = R.drawable.stat_sys_ringer_vibrate;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index a1022c4..de00237 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -37,7 +37,7 @@
 import com.android.systemui.qs.external.TileLifecycleManager;
 import com.android.systemui.qs.external.TileServices;
 import com.android.systemui.qs.tiles.AirplaneModeTile;
-import com.android.systemui.qs.tiles.BatteryTile;
+import com.android.systemui.qs.tiles.BatterySaverTile;
 import com.android.systemui.qs.tiles.BluetoothTile;
 import com.android.systemui.qs.tiles.CastTile;
 import com.android.systemui.qs.tiles.CellularTile;
@@ -273,7 +273,7 @@
         else if (tileSpec.equals("cast")) return new CastTile(this);
         else if (tileSpec.equals("hotspot")) return new HotspotTile(this);
         else if (tileSpec.equals("user")) return new UserTile(this);
-        else if (tileSpec.equals("battery")) return new BatteryTile(this);
+        else if (tileSpec.equals("battery")) return new BatterySaverTile(this);
         else if (tileSpec.equals("saver")) return new DataSaverTile(this);
         else if (tileSpec.equals("night")) return new NightDisplayTile(this);
         else if (tileSpec.equals("nfc")) return new NfcTile(this);
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 648e00c..c90d493 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -23,6 +23,7 @@
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
 
+import static com.android.systemui.statusbar.notification.NotificationInflater.InflationExceptionHandler;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -160,7 +161,6 @@
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NotificationContentView;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.NotificationGuts;
@@ -171,6 +171,7 @@
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
@@ -298,7 +299,7 @@
             "com.android.systemui.statusbar.banner_action_setup";
     private static final String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
             = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
-    static final String TAG = "StatusBar";
+    public static final String TAG = "StatusBar";
     public static final boolean DEBUG = false;
     public static final boolean SPEW = false;
     public static final boolean DUMPTRUCK = true; // extra dumpsys info
@@ -715,7 +716,8 @@
     private LogMaker mStatusBarStateLog;
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
     private NotificationIconAreaController mNotificationIconAreaController;
-    private ConfigurationListener mDensityChangeListener;
+    private ConfigurationListener mConfigurationListener;
+    private InflationExceptionHandler mInflationExceptionHandler = this::handleInflationException;
 
     private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
         final int N = array.size();
@@ -942,13 +944,18 @@
 
         Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this);
 
-        mDensityChangeListener = new ConfigurationListener() {
+        mConfigurationListener = new ConfigurationListener() {
+            @Override
+            public void onConfigChanged(Configuration newConfig) {
+                StatusBar.this.onConfigurationChanged(newConfig);
+            }
+
             @Override
             public void onDensityOrFontScaleChanged() {
                 StatusBar.this.onDensityOrFontScaleChanged();
             }
         };
-        Dependency.get(ConfigurationController.class).addCallback(mDensityChangeListener);
+        Dependency.get(ConfigurationController.class).addCallback(mConfigurationListener);
     }
 
     protected void createIconController() {
@@ -1275,12 +1282,11 @@
             Entry entry = activeNotifications.get(i);
             boolean exposedGuts = mNotificationGutsExposed != null
                     && entry.row.getGuts() == mNotificationGutsExposed;
-            entry.row.reInflateViews();
+            entry.row.onDensityOrFontScaleChanged();
             if (exposedGuts) {
                 mNotificationGutsExposed = entry.row.getGuts();
                 bindGuts(entry.row, mGutsMenuItem);
             }
-            inflateViews(entry, mStackScroller);
         }
         // end old BaseStatusBar.onDensityOrFontScaleChanged().
         mScrimController.onDensityOrFontScaleChanged();
@@ -1549,14 +1555,11 @@
     }
 
     public void addNotification(StatusBarNotification notification, RankingMap ranking,
-            Entry oldEntry) {
+            Entry oldEntry) throws InflationException {
         if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
 
         mNotificationData.updateRanking(ranking);
         Entry shadeEntry = createNotificationViews(notification);
-        if (shadeEntry == null) {
-            return;
-        }
         boolean isHeadsUped = shouldPeek(shadeEntry);
         if (isHeadsUped) {
             mHeadsUpManager.showNotification(shadeEntry);
@@ -1598,6 +1601,10 @@
         setAreThereNotifications();
     }
 
+    public void handleInflationException(StatusBarNotification notification, InflationException e) {
+        handleNotificationError(notification, e.getMessage());
+    }
+
     private boolean shouldSuppressFullScreenIntent(String key) {
         if (isDeviceInVrMode()) {
             return true;
@@ -1662,10 +1669,17 @@
                     sbn.getOpPkg(),
                     sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
                     newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
-
-            updateNotification(newSbn, null);
-            mKeysKeptForRemoteInput.add(entry.key);
-            return;
+            boolean updated = false;
+            try {
+                updateNotification(newSbn, null);
+                updated = true;
+            } catch (InflationException e) {
+                deferRemoval = false;
+            }
+            if (updated) {
+                mKeysKeptForRemoteInput.add(entry.key);
+                return;
+            }
         }
         if (deferRemoval) {
             mLatestRankingMap = ranking;
@@ -1807,15 +1821,6 @@
                 updatePublicContentView(ent, ent.notification);
             }
             ent.row.setSensitive(sensitive, deviceSensitive);
-            if (ent.autoRedacted && ent.legacy) {
-                // TODO: Also fade this? Or, maybe easier (and better), provide a dark redacted form
-                // for legacy auto redacted notifications.
-                if (showingPublic) {
-                    ent.row.setShowingLegacyBackground(false);
-                } else {
-                    ent.row.setShowingLegacyBackground(true);
-                }
-            }
             if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
                 ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
                         ent.row.getStatusBarNotification());
@@ -2033,10 +2038,10 @@
     }
 
     private void updateEmptyShadeView() {
-        boolean showEmptyShade =
+        boolean showEmptyShadeView =
                 mState != StatusBarState.KEYGUARD &&
                         mNotificationData.getActiveNotifications().size() == 0;
-        mNotificationPanel.setShadeEmpty(showEmptyShade);
+        mNotificationPanel.showEmptyShadeView(showEmptyShadeView);
     }
 
     private void updateSpeedBumpIndex() {
@@ -2719,8 +2724,8 @@
         return mLaunchTransitionFadingAway;
     }
 
-    public boolean shouldHideNotificationIcons() {
-        return mNotificationPanel.shouldHideNotificationIcons();
+    public boolean hideStatusBarIconsWhenExpanded() {
+        return mNotificationPanel.hideStatusBarIconsWhenExpanded();
     }
 
     /**
@@ -2962,7 +2967,7 @@
         runPostCollapseRunnables();
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
         showBouncerIfKeyguard();
-        recomputeDisableFlags(shouldAnimatIconHiding() /* animate */);
+        recomputeDisableFlags(mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
 
         // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
         // the bouncer appear animation.
@@ -2971,10 +2976,6 @@
         }
     }
 
-    private boolean shouldAnimatIconHiding() {
-        return mNotificationPanel.shouldAnimateIconHiding();
-    }
-
     public boolean interceptTouchEvent(MotionEvent event) {
         if (DEBUG_GESTURES) {
             if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
@@ -3909,7 +3910,7 @@
         }
         Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(null);
         mDeviceProvisionedController.removeCallback(mUserSetupObserver);
-        Dependency.get(ConfigurationController.class).removeCallback(mDensityChangeListener);
+        Dependency.get(ConfigurationController.class).removeCallback(mConfigurationListener);
     }
 
     private boolean mDemoModeAllowed;
@@ -5512,7 +5513,11 @@
                 @Override
                 public void run() {
                     for (StatusBarNotification sbn : notifications) {
-                        addNotification(sbn, currentRanking, null /* oldEntry */);
+                        try {
+                            addNotification(sbn, currentRanking, null /* oldEntry */);
+                        } catch (InflationException e) {
+                            handleInflationException(sbn, e);
+                        }
                     }
                 }
             });
@@ -5548,10 +5553,14 @@
                             }
                             return;
                         }
-                        if (isUpdate) {
-                            updateNotification(sbn, rankingMap);
-                        } else {
-                            addNotification(sbn, rankingMap, null /* oldEntry */);
+                        try {
+                            if (isUpdate) {
+                                updateNotification(sbn, rankingMap);
+                            } else {
+                                addNotification(sbn, rankingMap, null /* oldEntry */);
+                            }
+                        } catch (InflationException e) {
+                            handleInflationException(sbn, e);
                         }
                     }
                 });
@@ -5681,26 +5690,6 @@
         return mGroupManager;
     }
 
-    protected void bindDismissRunnable(final ExpandableNotificationRow row) {
-        row.setOnDismissRunnable(() -> performRemoveNotification(row.getStatusBarNotification()));
-    }
-
-    protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
-            NotificationData.Entry entry) {
-
-        if (entry.getContentView().getId()
-                != com.android.internal.R.id.status_bar_latest_event_content) {
-            // Using custom RemoteViews
-            if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
-                    && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
-                entry.row.setShowingLegacyBackground(true);
-                entry.legacy = true;
-            }
-        }
-
-        entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
-    }
-
     public boolean isMediaNotification(NotificationData.Entry entry) {
         // TODO: confirm that there's a valid media key
         return entry.getExpandedContentView() != null &&
@@ -6082,55 +6071,16 @@
         }
     }
 
-    protected boolean inflateViews(Entry entry, ViewGroup parent) {
+    protected void inflateViews(Entry entry, ViewGroup parent) throws
+            InflationException {
         PackageManager pmUser = getPackageManagerForUser(mContext,
                 entry.notification.getUser().getIdentifier());
 
         final StatusBarNotification sbn = entry.notification;
-        boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
-        boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
-                mNotificationData.getImportance(sbn.getKey()));
-        boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded;
-        try {
-            entry.cacheContentViews(mContext, null, isLowPriority, useIncreasedCollapsedHeight,
-                    useIncreasedHeadsUp);
-        } catch (RuntimeException e) {
-            Log.e(TAG, "Unable to get notification remote views", e);
-            return false;
-        }
-
-        final RemoteViews contentView = entry.cachedContentView;
-        final RemoteViews bigContentView = entry.cachedBigContentView;
-        final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
-        final RemoteViews publicContentView = entry.cachedPublicContentView;
-        final RemoteViews ambientContentView = entry.cachedAmbientContentView;
-
-        if (contentView == null) {
-            Log.v(TAG, "no contentView for: " + sbn.getNotification());
-            return false;
-        }
-
-        if (DEBUG) {
-            Log.v(TAG, "publicContentView: " + publicContentView);
-        }
-
         ExpandableNotificationRow row;
-
-        // Stash away previous user expansion state so we can restore it at
-        // the end.
-        boolean hasUserChangedExpansion = false;
-        boolean userExpanded = false;
-        boolean userLocked = false;
-
         if (entry.row != null) {
             row = entry.row;
-            hasUserChangedExpansion = row.hasUserChangedExpansion();
-            userExpanded = row.isUserExpanded();
-            userLocked = row.isUserLocked();
             entry.reset();
-            if (hasUserChangedExpansion) {
-                row.setUserExpanded(userExpanded);
-            }
         } else {
             // create the row view
             LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
@@ -6142,6 +6092,8 @@
             row.setHeadsUpManager(mHeadsUpManager);
             row.setRemoteInputController(mRemoteInputController);
             row.setOnExpandClickListener(this);
+            row.setRemoteViewClickHandler(mOnClickHandler);
+            row.setInflateExceptionHandler(mInflationExceptionHandler);
 
             // Get the app name.
             // Note that Notification.Builder#bindHeaderAppName has similar logic
@@ -6161,86 +6113,19 @@
                 // Do nothing
             }
             row.setAppName(appname);
+            row.setOnDismissRunnable(() ->
+                    performRemoveNotification(row.getStatusBarNotification()));
+            row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+            if (ENABLE_REMOTE_INPUT) {
+                row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
+            }
         }
 
-        bindDismissRunnable(row);
+        boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
         row.setIsLowPriority(isLowPriority);
-
-        // NB: the large icon is now handled entirely by the template
-
         // bind the click event to the content area
-        NotificationContentView contentContainer = row.getPrivateLayout();
-        NotificationContentView contentContainerPublic = row.getPublicLayout();
-
-        row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-        if (ENABLE_REMOTE_INPUT) {
-            row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
-        }
-
         mNotificationClicker.register(row, sbn);
 
-        // set up the adaptive layout
-        View contentViewLocal = null;
-        View bigContentViewLocal = null;
-        View headsUpContentViewLocal = null;
-        View publicViewLocal = null;
-        View ambientViewLocal = null;
-        try {
-            contentViewLocal = contentView.apply(
-                    sbn.getPackageContext(mContext),
-                    contentContainer,
-                    mOnClickHandler);
-            if (bigContentView != null) {
-                bigContentViewLocal = bigContentView.apply(
-                        sbn.getPackageContext(mContext),
-                        contentContainer,
-                        mOnClickHandler);
-            }
-            if (headsUpContentView != null) {
-                headsUpContentViewLocal = headsUpContentView.apply(
-                        sbn.getPackageContext(mContext),
-                        contentContainer,
-                        mOnClickHandler);
-            }
-            if (publicContentView != null) {
-                publicViewLocal = publicContentView.apply(
-                        sbn.getPackageContext(mContext),
-                        contentContainerPublic, mOnClickHandler);
-            }
-            if (ambientContentView != null) {
-                ambientViewLocal = ambientContentView.apply(
-                        sbn.getPackageContext(mContext),
-                        contentContainer, mOnClickHandler);
-            }
-
-            if (contentViewLocal != null) {
-                contentViewLocal.setIsRootNamespace(true);
-                contentContainer.setContractedChild(contentViewLocal);
-            }
-            if (bigContentViewLocal != null) {
-                bigContentViewLocal.setIsRootNamespace(true);
-                contentContainer.setExpandedChild(bigContentViewLocal);
-            }
-            if (headsUpContentViewLocal != null) {
-                headsUpContentViewLocal.setIsRootNamespace(true);
-                contentContainer.setHeadsUpChild(headsUpContentViewLocal);
-            }
-            if (publicViewLocal != null) {
-                publicViewLocal.setIsRootNamespace(true);
-                contentContainerPublic.setContractedChild(publicViewLocal);
-            }
-
-            if (ambientViewLocal != null) {
-                ambientViewLocal.setIsRootNamespace(true);
-                contentContainer.setAmbientChild(ambientViewLocal);
-            }
-        }
-        catch (RuntimeException e) {
-            final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
-            Log.e(TAG, "couldn't inflate view for notification " + ident, e);
-            return false;
-        }
-
         // Extract target SDK version.
         try {
             ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
@@ -6248,25 +6133,20 @@
         } catch (NameNotFoundException ex) {
             Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
         }
+        row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
+                && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
+        entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
         entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
 
         entry.row = row;
         entry.row.setOnActivatedListener(this);
-        entry.row.setExpandable(bigContentViewLocal != null);
 
-        applyColorsAndBackgrounds(sbn, entry);
-
-        // Restore previous flags.
-        if (hasUserChangedExpansion) {
-            // Note: setUserExpanded() conveniently ignores calls with
-            //       userExpanded=true if !isExpandable().
-            row.setUserExpanded(userExpanded);
-        }
-        row.setUserLocked(userLocked);
+        boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
+                mNotificationData.getImportance(sbn.getKey()));
+        boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded;
         row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
         row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
-        row.onNotificationUpdated(entry);
-        return true;
+        row.updateNotification(entry);
     }
 
     /**
@@ -6604,23 +6484,17 @@
         return entry.notification;
     }
 
-    protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
+    protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
+            throws InflationException {
         if (DEBUG) {
             Log.d(TAG, "createNotificationViews(notification=" + sbn);
         }
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
         Dependency.get(LeakDetector.class).trackInstance(entry);
-        try {
-            entry.createIcons(mContext, sbn);
-        } catch (NotificationData.IconException exception) {
-            handleNotificationError(sbn, exception.getMessage());
-        }
+        entry.createIcons(mContext, sbn);
 
         // Construct the expanded view.
-        if (!inflateViews(entry, mStackScroller)) {
-            handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
-            return null;
-        }
+        inflateViews(entry, mStackScroller);
         return entry;
     }
 
@@ -6696,6 +6570,7 @@
                 }
             }
         }
+        mNotificationPanel.setNoVisibleNotifications(visibleNotifications == 0);
 
         mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
         mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
@@ -6741,7 +6616,8 @@
         }
     }
 
-    public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
+    public void updateNotification(StatusBarNotification notification, RankingMap ranking)
+            throws InflationException {
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final String key = notification.getKey();
@@ -6756,56 +6632,16 @@
         Notification n = notification.getNotification();
         mNotificationData.updateRanking(ranking);
 
-        boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(notification,
-                mNotificationData.getImportance(notification.getKey()));
-        entry.row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
-        boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded;
-        entry.row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
-        boolean applyInPlace;
-        try {
-            applyInPlace = entry.cacheContentViews(mContext, notification.getNotification(),
-                    mNotificationData.isAmbient(key), useIncreasedCollapsedHeight,
-                    useIncreasedHeadsUp);
-        } catch (RuntimeException e) {
-            Log.e(TAG, "Unable to get notification remote views", e);
-            applyInPlace = false;
-        }
-        boolean shouldPeek = shouldPeek(entry, notification);
-        boolean alertAgain = alertAgain(entry, n);
-        if (DEBUG) {
-            Log.d(TAG, "applyInPlace=" + applyInPlace
-                    + " shouldPeek=" + shouldPeek
-                    + " alertAgain=" + alertAgain);
-        }
-
         final StatusBarNotification oldNotification = entry.notification;
         entry.notification = notification;
         mGroupManager.onEntryUpdated(entry, oldNotification);
 
-        boolean updateSuccessful = false;
-        try {
-            if (applyInPlace) {
-                if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
-                try {
-                    entry.updateIcons(mContext, n);
-                    updateNotificationViews(entry, notification);
-                    updateSuccessful = true;
-                } catch (RuntimeException e) {
-                    // It failed to apply cleanly.
-                    Log.w(TAG, "Couldn't reapply views for package " +
-                            notification.getPackageName(), e);
-                }
-            }
-            if (!updateSuccessful) {
-                entry.updateIcons(mContext, n);
-                if (!inflateViews(entry, mStackScroller)) {
-                    handleNotificationError(notification, "Couldn't update remote views for: "
-                            + notification);
-                }
-            }
-        } catch (NotificationData.IconException e) {
-            handleNotificationError(notification, e.getMessage());
-        }
+        entry.updateIcons(mContext, n);
+        inflateViews(entry, mStackScroller);
+
+        boolean shouldPeek = shouldPeek(entry, notification);
+        boolean alertAgain = alertAgain(entry, n);
+
         updateHeadsUp(key, entry, shouldPeek, alertAgain);
         updateNotifications();
 
@@ -6820,44 +6656,9 @@
             boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
             Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
         }
-
         setAreThereNotifications();
     }
 
-    private void updateNotificationViews(Entry entry, StatusBarNotification sbn) {
-        final RemoteViews contentView = entry.cachedContentView;
-        final RemoteViews bigContentView = entry.cachedBigContentView;
-        final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
-        final RemoteViews publicContentView = entry.cachedPublicContentView;
-        final RemoteViews ambientContentView = entry.cachedAmbientContentView;
-
-        // Reapply the RemoteViews
-        contentView.reapply(mContext, entry.getContentView(), mOnClickHandler);
-        if (bigContentView != null && entry.getExpandedContentView() != null) {
-            bigContentView.reapply(sbn.getPackageContext(mContext),
-                    entry.getExpandedContentView(),
-                    mOnClickHandler);
-        }
-        View headsUpChild = entry.getHeadsUpContentView();
-        if (headsUpContentView != null && headsUpChild != null) {
-            headsUpContentView.reapply(sbn.getPackageContext(mContext),
-                    headsUpChild, mOnClickHandler);
-        }
-        if (publicContentView != null && entry.getPublicContentView() != null) {
-            publicContentView.reapply(sbn.getPackageContext(mContext),
-                    entry.getPublicContentView(), mOnClickHandler);
-        }
-        if (ambientContentView != null && entry.getAmbientContentView() != null) {
-            ambientContentView.reapply(sbn.getPackageContext(mContext),
-                    entry.getAmbientContentView(), mOnClickHandler);
-        }
-        // update the contentIntent
-        mNotificationClicker.register(entry.row, sbn);
-
-        entry.row.onNotificationUpdated(entry);
-        entry.row.resetHeight();
-    }
-
     protected void updatePublicContentView(Entry entry,
             StatusBarNotification sbn) {
         final RemoteViews publicContentView = entry.cachedPublicContentView;
@@ -6871,10 +6672,7 @@
             TextView titleView = (TextView) inflatedView.findViewById(android.R.id.title);
             if (titleView != null
                     && !titleView.getText().toString().equals(notificationHiddenText)) {
-                publicContentView.setTextViewText(android.R.id.title, notificationHiddenText);
-                publicContentView.reapply(sbn.getPackageContext(mContext),
-                        inflatedView, mOnClickHandler);
-                entry.row.onNotificationUpdated(entry);
+                titleView.setText(notificationHiddenText);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
index 821e635..a87b50a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
@@ -116,19 +116,20 @@
     }
 
     private void notifyKeyguardChanged() {
-        mCallbacks.forEach(Callback::onKeyguardShowingChanged);
+        // Copy the list to allow removal during callback.
+        new ArrayList<Callback>(mCallbacks).forEach(Callback::onKeyguardShowingChanged);
     }
 
     public void notifyKeyguardFadingAway(long delay, long fadeoutDuration) {
         mKeyguardFadingAway = true;
         mKeyguardFadingAwayDelay = delay;
         mKeyguardFadingAwayDuration = fadeoutDuration;
-        mCallbacks.forEach(Callback::onKeyguardShowingChanged);
+        notifyKeyguardChanged();
     }
 
     public void notifyKeyguardDoneFading() {
         mKeyguardFadingAway = false;
-        mCallbacks.forEach(Callback::onKeyguardShowingChanged);
+        notifyKeyguardChanged();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 51d931e..bc3eec9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -39,6 +39,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.MathUtils;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.PhoneConstants;
@@ -78,6 +79,7 @@
     private static final int EMERGENCY_FIRST_CONTROLLER = 100;
     private static final int EMERGENCY_VOICE_CONTROLLER = 200;
     private static final int EMERGENCY_NO_SUB = 300;
+    private static final int EMERGENCY_ASSUMED_VOICE_CONTROLLER = 400;
 
     private final Context mContext;
     private final TelephonyManager mPhone;
@@ -99,8 +101,7 @@
     final EthernetSignalController mEthernetSignalController;
 
     @VisibleForTesting
-    final Map<Integer, MobileSignalController> mMobileSignalControllers =
-            new HashMap<Integer, MobileSignalController>();
+    final SparseArray<MobileSignalController> mMobileSignalControllers = new SparseArray<>();
     // When no SIMs are around at setup, and one is added later, it seems to default to the first
     // SIM for most actions.  This may be null if there aren't any SIMs around.
     private MobileSignalController mDefaultSignalController;
@@ -232,7 +233,8 @@
     }
 
     private void registerListeners() {
-        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
             mobileSignalController.registerListener();
         }
         if (mSubscriptionListener == null) {
@@ -261,7 +263,8 @@
 
     private void unregisterListeners() {
         mListening = false;
-        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
             mobileSignalController.unregisterListener();
         }
         mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener);
@@ -305,7 +308,7 @@
             if (DEBUG) Log.e(TAG, "No data sim selected");
             return mDefaultSignalController;
         }
-        if (mMobileSignalControllers.containsKey(dataSubId)) {
+        if (mMobileSignalControllers.indexOfKey(dataSubId) >= 0) {
             return mMobileSignalControllers.get(dataSubId);
         }
         if (DEBUG) Log.e(TAG, "Cannot find controller for data sub: " + dataSubId);
@@ -326,8 +329,8 @@
         }
         int voiceSubId = mSubDefaults.getDefaultVoiceSubId();
         if (!SubscriptionManager.isValidSubscriptionId(voiceSubId)) {
-            for (MobileSignalController mobileSignalController :
-                                            mMobileSignalControllers.values()) {
+            for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+                MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
                 if (!mobileSignalController.getState().isEmergency) {
                     mEmergencySource = EMERGENCY_FIRST_CONTROLLER
                             + mobileSignalController.mSubscriptionInfo.getSubscriptionId();
@@ -336,11 +339,20 @@
                 }
             }
         }
-        if (mMobileSignalControllers.containsKey(voiceSubId)) {
+        if (mMobileSignalControllers.indexOfKey(voiceSubId) >= 0) {
             mEmergencySource = EMERGENCY_VOICE_CONTROLLER + voiceSubId;
             if (DEBUG) Log.d(TAG, "Getting emergency from " + voiceSubId);
             return mMobileSignalControllers.get(voiceSubId).getState().isEmergency;
         }
+        // If we have the wrong subId but there is only one sim anyway, assume it should be the
+        // default.
+        if (mMobileSignalControllers.size() == 1) {
+            mEmergencySource = EMERGENCY_ASSUMED_VOICE_CONTROLLER
+                    + mMobileSignalControllers.keyAt(0);
+            if (DEBUG) Log.d(TAG, "Getting assumed emergency from "
+                    + mMobileSignalControllers.keyAt(0));
+            return mMobileSignalControllers.valueAt(0).getState().isEmergency;
+        }
         if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId);
         mEmergencySource = EMERGENCY_NO_SUB + voiceSubId;
         // Something is wrong, better assume we can't make calls...
@@ -363,7 +375,8 @@
         cb.setNoSims(mHasNoSims);
         mWifiSignalController.notifyListeners(cb);
         mEthernetSignalController.notifyListeners(cb);
-        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
             mobileSignalController.notifyListeners(cb);
         }
         mCallbackHandler.setListening(cb, true);
@@ -416,7 +429,8 @@
         } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
             // Notify every MobileSignalController so they can know whether they are the
             // data sim or not.
-            for (MobileSignalController controller : mMobileSignalControllers.values()) {
+            for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+                MobileSignalController controller = mMobileSignalControllers.valueAt(i);
                 controller.handleBroadcast(intent);
             }
         } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
@@ -433,7 +447,7 @@
             int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
                     SubscriptionManager.INVALID_SUBSCRIPTION_ID);
             if (SubscriptionManager.isValidSubscriptionId(subId)) {
-                if (mMobileSignalControllers.containsKey(subId)) {
+                if (mMobileSignalControllers.indexOfKey(subId) >= 0) {
                     mMobileSignalControllers.get(subId).handleBroadcast(intent);
                 } else {
                     // Can't find this subscription...  We must be out of date.
@@ -458,8 +472,9 @@
 
     @VisibleForTesting
     void handleConfigurationChanged() {
-        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
-            mobileSignalController.setConfiguration(mConfig);
+        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+            MobileSignalController controller = mMobileSignalControllers.valueAt(i);
+            controller.setConfiguration(mConfig);
         }
         refreshLocale();
     }
@@ -511,15 +526,20 @@
         });
         mCurrentSubscriptions = subscriptions;
 
-        HashMap<Integer, MobileSignalController> cachedControllers =
-                new HashMap<Integer, MobileSignalController>(mMobileSignalControllers);
+        SparseArray<MobileSignalController> cachedControllers =
+                new SparseArray<MobileSignalController>();
+        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+            cachedControllers.put(mMobileSignalControllers.keyAt(i),
+                    mMobileSignalControllers.valueAt(i));
+        }
         mMobileSignalControllers.clear();
         final int num = subscriptions.size();
         for (int i = 0; i < num; i++) {
             int subId = subscriptions.get(i).getSubscriptionId();
             // If we have a copy of this controller already reuse it, otherwise make a new one.
-            if (cachedControllers.containsKey(subId)) {
-                mMobileSignalControllers.put(subId, cachedControllers.remove(subId));
+            if (cachedControllers.indexOfKey(subId) >= 0) {
+                mMobileSignalControllers.put(subId, cachedControllers.get(subId));
+                cachedControllers.remove(subId);
             } else {
                 MobileSignalController controller = new MobileSignalController(mContext, mConfig,
                         mHasMobileDataFeature, mPhone, mCallbackHandler,
@@ -535,7 +555,8 @@
             }
         }
         if (mListening) {
-            for (Integer key : cachedControllers.keySet()) {
+            for (int i = 0; i < cachedControllers.size(); i++) {
+                int key = cachedControllers.keyAt(i);
                 if (cachedControllers.get(key) == mDefaultSignalController) {
                     mDefaultSignalController = null;
                 }
@@ -557,7 +578,8 @@
 
     private void handleSetUserSetupComplete(boolean userSetup) {
         mUserSetup = userSetup;
-        for (MobileSignalController controller : mMobileSignalControllers.values()) {
+        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+            MobileSignalController controller = mMobileSignalControllers.valueAt(i);
             controller.setUserSetupComplete(mUserSetup);
         }
     }
@@ -568,7 +590,7 @@
             return false;
         }
         for (SubscriptionInfo info : allSubscriptions) {
-            if (!mMobileSignalControllers.containsKey(info.getSubscriptionId())) {
+            if (mMobileSignalControllers.indexOfKey(info.getSubscriptionId()) < 0) {
                 return false;
             }
         }
@@ -580,7 +602,8 @@
                 Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
         if (airplaneMode != mAirplaneMode || force) {
             mAirplaneMode = airplaneMode;
-            for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+            for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+                MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
                 mobileSignalController.setAirplaneMode(mAirplaneMode);
             }
             notifyListeners();
@@ -601,7 +624,8 @@
      */
     private void notifyAllListeners() {
         notifyListeners();
-        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
             mobileSignalController.notifyListeners();
         }
         mWifiSignalController.notifyListeners();
@@ -650,7 +674,8 @@
      */
     private void pushConnectivityToSignals() {
         // We want to update all the icons, all at once, for any condition change
-        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
             mobileSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
         }
         mWifiSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
@@ -682,7 +707,8 @@
         pw.print("  mEmergencySource=");
         pw.println(emergencyToString(mEmergencySource));
 
-        for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
+        for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+            MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
             mobileSignalController.dump(pw);
         }
         mWifiSignalController.dump(pw);
@@ -694,6 +720,9 @@
 
     private static final String emergencyToString(int emergencySource) {
         if (emergencySource > EMERGENCY_NO_SUB) {
+            return "ASSUMED_VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER)
+                    + ")";
+        } else if (emergencySource > EMERGENCY_NO_SUB) {
             return "NO_SUB(" + (emergencySource - EMERGENCY_NO_SUB) + ")";
         } else if (emergencySource > EMERGENCY_VOICE_CONTROLLER) {
             return "VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER) + ")";
@@ -724,7 +753,8 @@
             // Update what MobileSignalControllers, because they may change
             // to set the number of sim slots.
             updateMobileControllers();
-            for (MobileSignalController controller : mMobileSignalControllers.values()) {
+            for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+                MobileSignalController controller = mMobileSignalControllers.valueAt(i);
                 controller.resetLastState();
             }
             mWifiSignalController.resetLastState();
@@ -747,7 +777,8 @@
                     connected.set(mWifiSignalController.mTransportType);
                 }
                 mWifiSignalController.updateConnectivity(connected, connected);
-                for (MobileSignalController controller : mMobileSignalControllers.values()) {
+                for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+                    MobileSignalController controller = mMobileSignalControllers.valueAt(i);
                     if (mDemoInetCondition) {
                         connected.set(controller.mTransportType);
                     }
@@ -820,8 +851,7 @@
                     mCallbackHandler.setSubs(subs);
                 }
                 // Hack to index linearly for easy use.
-                MobileSignalController controller = mMobileSignalControllers
-                        .values().toArray(new MobileSignalController[0])[slot];
+                MobileSignalController controller = mMobileSignalControllers.valueAt(slot);
                 controller.getState().dataSim = datatype != null;
                 controller.getState().isDefault = datatype != null;
                 controller.getState().dataConnected = datatype != null;
@@ -875,7 +905,8 @@
             String carrierNetworkChange = args.getString("carriernetworkchange");
             if (carrierNetworkChange != null) {
                 boolean show = carrierNetworkChange.equals("show");
-                for (MobileSignalController controller : mMobileSignalControllers.values()) {
+                for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+                    MobileSignalController controller = mMobileSignalControllers.valueAt(i);
                     controller.setCarrierNetworkChangeMode(show);
                 }
             }
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 e6a3add..83cbd72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -52,6 +52,14 @@
     private static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
     private static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
     private static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
+    private static final AnimationProperties ALPHA_FADE_IN = new AnimationProperties() {
+        private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
+
+        @Override
+        public AnimationFilter getAnimationFilter() {
+            return mAnimationFilter;
+        }
+    }.setDuration(200);
 
     private final List<View> mDividers = new ArrayList<>();
     private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
@@ -64,7 +72,7 @@
     private float mCollapsedBottompadding;
     private ViewInvertHelper mOverflowInvertHelper;
     private boolean mChildrenExpanded;
-    private ExpandableNotificationRow mNotificationParent;
+    private ExpandableNotificationRow mContainingNotification;
     private TextView mOverflowNumber;
     private ViewState mGroupOverFlowState;
     private int mRealHeight;
@@ -75,10 +83,15 @@
 
     private NotificationHeaderView mNotificationHeader;
     private NotificationViewWrapper mNotificationHeaderWrapper;
+    private NotificationHeaderView mNotificationHeaderLowPriority;
+    private NotificationViewWrapper mNotificationHeaderWrapperLowPriority;
     private NotificationHeaderUtil mHeaderUtil;
     private ViewState mHeaderViewState;
     private int mClipBottomAmount;
     private boolean mIsLowPriority;
+    private boolean mHeaderVisible = true;
+    private OnClickListener mHeaderClickListener;
+    private boolean mShowingNormalHeader;
 
     public NotificationChildrenContainer(Context context) {
         this(context, null);
@@ -135,6 +148,11 @@
             mNotificationHeader.layout(0, 0, mNotificationHeader.getMeasuredWidth(),
                     mNotificationHeader.getMeasuredHeight());
         }
+        if (mNotificationHeaderLowPriority != null) {
+            mNotificationHeaderLowPriority.layout(0, 0,
+                    mNotificationHeaderLowPriority.getMeasuredWidth(),
+                    mNotificationHeaderLowPriority.getMeasuredHeight());
+        }
     }
 
     @Override
@@ -178,10 +196,14 @@
             height = Math.min(height, size);
         }
 
+        int headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
         if (mNotificationHeader != null) {
-            int headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
             mNotificationHeader.measure(widthMeasureSpec, headerHeightSpec);
         }
+        if (mNotificationHeaderLowPriority != null) {
+            headerHeightSpec = MeasureSpec.makeMeasureSpec(mHeaderHeight, MeasureSpec.EXACTLY);
+            mNotificationHeaderLowPriority.measure(widthMeasureSpec, headerHeightSpec);
+        }
 
         setMeasuredDimension(width, height);
     }
@@ -247,30 +269,66 @@
         return mChildren.size();
     }
 
-    public void recreateNotificationHeader(OnClickListener listener,
-            StatusBarNotification notification) {
+    public void recreateNotificationHeader(OnClickListener listener) {
+        mHeaderClickListener = listener;
+        StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
         final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
-                mNotificationParent.getStatusBarNotification().getNotification());
-        final RemoteViews header = mIsLowPriority
-                ? builder.makeLowPriorityContentView(true /* useRegularSubtext */)
-                : builder.makeNotificationHeader();
+                notification.getNotification());
+        RemoteViews header = builder.makeNotificationHeader();
         if (mNotificationHeader == null) {
             mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this);
             final View expandButton = mNotificationHeader.findViewById(
                     com.android.internal.R.id.expand_button);
             expandButton.setVisibility(VISIBLE);
-            mNotificationHeader.setOnClickListener(listener);
+            mNotificationHeader.setOnClickListener(mHeaderClickListener);
             mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
-                    mNotificationHeader, mNotificationParent);
+                    mNotificationHeader, mContainingNotification);
             addView(mNotificationHeader, 0);
             invalidate();
         } else {
             header.reapply(getContext(), mNotificationHeader);
-            mNotificationHeaderWrapper.notifyContentUpdated(notification, mIsLowPriority);
         }
+        mNotificationHeaderWrapper.notifyContentUpdated(mContainingNotification);
+        recreateLowPriorityHeader(builder);
+        updateHeaderVisibility(false /* animate */);
         updateChildrenHeaderAppearance();
     }
 
+    /**
+     * Recreate the low-priority header.
+     *
+     * @param builder a builder to reuse. Otherwise the builder will be recovered.
+     */
+    private void recreateLowPriorityHeader(Notification.Builder builder) {
+        RemoteViews header;
+        StatusBarNotification notification = mContainingNotification.getStatusBarNotification();
+        if (mIsLowPriority) {
+            if (builder == null) {
+                builder = Notification.Builder.recoverBuilder(getContext(),
+                        notification.getNotification());
+            }
+            header = builder.makeLowPriorityContentView(true /* useRegularSubtext */);
+            if (mNotificationHeaderLowPriority == null) {
+                mNotificationHeaderLowPriority = (NotificationHeaderView) header.apply(getContext(),
+                        this);
+                final View expandButton = mNotificationHeaderLowPriority.findViewById(
+                        com.android.internal.R.id.expand_button);
+                expandButton.setVisibility(VISIBLE);
+                mNotificationHeaderLowPriority.setOnClickListener(mHeaderClickListener);
+                mNotificationHeaderWrapperLowPriority = NotificationViewWrapper.wrap(getContext(),
+                        mNotificationHeaderLowPriority, mContainingNotification);
+                addView(mNotificationHeaderLowPriority, 0);
+                invalidate();
+            } else {
+                header.reapply(getContext(), mNotificationHeaderLowPriority);
+            }
+            mNotificationHeaderWrapperLowPriority.notifyContentUpdated(mContainingNotification);
+        } else {
+            mNotificationHeaderLowPriority = null;
+            mNotificationHeaderWrapperLowPriority = null;
+        }
+    }
+
     public void updateChildrenHeaderAppearance() {
         mHeaderUtil.updateChildrenHeaderAppearance();
     }
@@ -371,9 +429,6 @@
      * @return the intrinsic size of this children container, i.e the natural fully expanded state
      */
     public int getIntrinsicHeight() {
-        if (mIsLowPriority && !mChildrenExpanded) {
-            return mNotificationHeader.getHeight();
-        }
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
         return getIntrinsicHeight(maxAllowedVisibleChildren);
     }
@@ -383,6 +438,9 @@
      *         in @param maxAllowedVisibleChildren
      */
     private int getIntrinsicHeight(float maxAllowedVisibleChildren) {
+        if (showingAsLowPriority()) {
+            return mNotificationHeaderLowPriority.getHeight();
+        }
         int intrinsicHeight = mNotificationHeaderMargin;
         int visibleChildren = 0;
         int childCount = mChildren.size();
@@ -442,24 +500,25 @@
         int lastVisibleIndex = maxAllowedVisibleChildren - 1;
         int firstOverflowIndex = lastVisibleIndex + 1;
         float expandFactor = 0;
+        boolean expandingToExpandedGroup = mUserLocked && !showingAsLowPriority();
         if (mUserLocked) {
             expandFactor = getGroupExpandFraction();
             firstOverflowIndex = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
         }
 
-        boolean childrenExpanded = !mNotificationParent.isGroupExpansionChanging()
+        boolean childrenExpanded = !mContainingNotification.isGroupExpansionChanging()
                 && mChildrenExpanded;
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             if (!firstChild) {
-                if (mUserLocked) {
+                if (expandingToExpandedGroup) {
                     yPosition += NotificationUtils.interpolate(mChildPadding, mDividerHeight,
                             expandFactor);
                 } else {
                     yPosition += mChildrenExpanded ? mDividerHeight : mChildPadding;
                 }
             } else {
-                if (mUserLocked) {
+                if (expandingToExpandedGroup) {
                     yPosition += NotificationUtils.interpolate(
                             0,
                             mNotificatonTopPadding + mDividerHeight,
@@ -478,7 +537,7 @@
             // When the group is expanded, the children cast the shadows rather than the parent
             // so use the parent's elevation here.
             childState.zTranslation = childrenExpanded
-                    ? mNotificationParent.getTranslationZ()
+                    ? mContainingNotification.getTranslationZ()
                     : 0;
             childState.dimmed = parentState.dimmed;
             childState.dark = parentState.dark;
@@ -487,7 +546,7 @@
             childState.clipTopAmount = 0;
             childState.alpha = 0;
             if (i < firstOverflowIndex) {
-                childState.alpha = mIsLowPriority && !mChildrenExpanded ? expandFactor : 1.0f;
+                childState.alpha = showingAsLowPriority() ? expandFactor : 1.0f;
             } else if (expandFactor == 1.0f && i <= lastVisibleIndex) {
                 childState.alpha = (mActualHeight - childState.yTranslation) / childState.height;
                 childState.alpha = Math.max(0.0f, Math.min(1.0f, childState.alpha));
@@ -526,7 +585,7 @@
             }
             mHeaderViewState.initFrom(mNotificationHeader);
             mHeaderViewState.zTranslation = childrenExpanded
-                    ? mNotificationParent.getTranslationZ()
+                    ? mContainingNotification.getTranslationZ()
                     : 0;
         }
     }
@@ -561,11 +620,11 @@
     }
 
     private int getMaxAllowedVisibleChildren(boolean likeCollapsed) {
-        if (!likeCollapsed && (mChildrenExpanded || mNotificationParent.isUserLocked())) {
+        if (!likeCollapsed && (mChildrenExpanded || mContainingNotification.isUserLocked())) {
             return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
         }
-        if (!mNotificationParent.isOnKeyguard()
-                && (mNotificationParent.isExpanded() || mNotificationParent.isHeadsUp())) {
+        if (mIsLowPriority || !mContainingNotification.isOnKeyguard()
+                && (mContainingNotification.isExpanded() || mContainingNotification.isHeadsUp())) {
             return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
         }
         return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
@@ -578,8 +637,8 @@
         if (mUserLocked) {
             expandFraction = getGroupExpandFraction();
         }
-        final boolean dividersVisible = mUserLocked
-                || mNotificationParent.isGroupExpansionChanging();
+        final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
+                || mContainingNotification.isGroupExpansionChanging();
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             ExpandableViewState viewState = state.getViewStateForView(child);
@@ -590,7 +649,7 @@
             tmpState.initFrom(divider);
             tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
             float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
-            if (mUserLocked && viewState.alpha != 0) {
+            if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
                 alpha = NotificationUtils.interpolate(0, 0.5f,
                         Math.min(viewState.alpha, expandFraction));
             }
@@ -612,7 +671,7 @@
 
     private void updateChildrenClipping() {
         int childCount = mChildren.size();
-        int layoutEnd = mNotificationParent.getActualHeight() - mClipBottomAmount;
+        int layoutEnd = mContainingNotification.getActualHeight() - mClipBottomAmount;
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             if (child.getVisibility() == GONE) {
@@ -652,8 +711,8 @@
         int childCount = mChildren.size();
         ViewState tmpState = new ViewState();
         float expandFraction = getGroupExpandFraction();
-        final boolean dividersVisible = mUserLocked
-                || mNotificationParent.isGroupExpansionChanging();
+        final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
+                || mContainingNotification.isGroupExpansionChanging();
         for (int i = childCount - 1; i >= 0; i--) {
             ExpandableNotificationRow child = mChildren.get(i);
             ExpandableViewState viewState = state.getViewStateForView(child);
@@ -664,7 +723,7 @@
             tmpState.initFrom(divider);
             tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
             float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
-            if (mUserLocked && viewState.alpha != 0) {
+            if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
                 alpha = NotificationUtils.interpolate(0, 0.5f,
                         Math.min(viewState.alpha, expandFraction));
             }
@@ -718,23 +777,100 @@
         }
     }
 
-    public void setNotificationParent(ExpandableNotificationRow parent) {
-        mNotificationParent = parent;
-        mHeaderUtil = new NotificationHeaderUtil(mNotificationParent);
+    public void setContainingNotification(ExpandableNotificationRow parent) {
+        mContainingNotification = parent;
+        mHeaderUtil = new NotificationHeaderUtil(mContainingNotification);
     }
 
-    public ExpandableNotificationRow getNotificationParent() {
-        return mNotificationParent;
+    public ExpandableNotificationRow getContainingNotification() {
+        return mContainingNotification;
     }
 
     public NotificationHeaderView getHeaderView() {
         return mNotificationHeader;
     }
 
-    public void updateHeaderVisibility(int visiblity) {
-        if (mNotificationHeader != null) {
-            mNotificationHeader.setVisibility(visiblity);
+    public NotificationHeaderView getLowPriorityHeaderView() {
+        return mNotificationHeaderLowPriority;
+    }
+
+    public void setHeaderVisible(boolean visible) {
+        mHeaderVisible = visible;
+        updateHeaderVisibility(false /* animate */);
+    }
+
+    private void updateHeaderVisibility(boolean animate) {
+        NotificationHeaderView visibleHeader = mNotificationHeader;
+        NotificationHeaderView hiddenHeader = mNotificationHeaderLowPriority;
+        boolean normalHeaderVisible = true;
+        if (showingAsLowPriority()) {
+            visibleHeader = mNotificationHeaderLowPriority;
+            hiddenHeader = mNotificationHeader;
+            normalHeaderVisible = false;
         }
+        if (animate) {
+            if (mHeaderVisible && visibleHeader != null && hiddenHeader != null
+                    && mShowingNormalHeader != normalHeaderVisible) {
+                hiddenHeader.setVisibility(VISIBLE);
+                visibleHeader.setVisibility(VISIBLE);
+                NotificationViewWrapper visibleWrapper = getWrapperForView(visibleHeader);
+                NotificationViewWrapper hiddenWrapper = getWrapperForView(hiddenHeader);
+                visibleWrapper.transformFrom(hiddenWrapper);
+                hiddenWrapper.transformTo(visibleWrapper, () -> updateHeaderVisibility(false));
+                startChildAlphaAnimations(normalHeaderVisible);
+            } else {
+                animate = false;
+            }
+        }
+        if (!animate) {
+            if (visibleHeader != null) {
+                getWrapperForView(visibleHeader).setVisible(true);
+                visibleHeader.setVisibility(mHeaderVisible ? VISIBLE : INVISIBLE);
+            }
+            if (hiddenHeader != null) {
+                getWrapperForView(hiddenHeader).setVisible(false);
+                hiddenHeader.setVisibility(INVISIBLE);
+            }
+        }
+        mShowingNormalHeader = normalHeaderVisible;
+    }
+
+    private void startChildAlphaAnimations(boolean toVisible) {
+        float target = toVisible ? 1.0f : 0.0f;
+        float start = 1.0f - target;
+        int childCount = mChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            if (i >= NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED) {
+                break;
+            }
+            ExpandableNotificationRow child = mChildren.get(i);
+            child.setAlpha(start);
+            ViewState viewState = new ViewState();
+            viewState.initFrom(child);
+            viewState.alpha = target;
+            ALPHA_FADE_IN.setDelay(i * 50);
+            viewState.animateTo(child, ALPHA_FADE_IN);
+        }
+    }
+
+
+    private void updateHeaderTransformation() {
+        if (mUserLocked && mHeaderVisible && showingAsLowPriority()) {
+            float fraction = getGroupExpandFraction();
+            mNotificationHeaderWrapper.transformFrom(mNotificationHeaderWrapperLowPriority,
+                    fraction);
+            mNotificationHeader.setVisibility(VISIBLE);
+            mNotificationHeaderWrapperLowPriority.transformTo(mNotificationHeaderWrapper,
+                    fraction);
+        }
+
+    }
+
+    private NotificationViewWrapper getWrapperForView(NotificationHeaderView visibleHeader) {
+        if (visibleHeader == mNotificationHeader) {
+            return mNotificationHeaderWrapper;
+        }
+        return mNotificationHeaderWrapperLowPriority;
     }
 
     /**
@@ -746,7 +882,7 @@
         if (mNotificationHeader != null) {
             if (expanded) {
                 ColorDrawable cd = new ColorDrawable();
-                cd.setColor(mNotificationParent.calculateBgColor());
+                cd.setColor(mContainingNotification.calculateBgColor());
                 mNotificationHeader.setHeaderBackgroundDrawable(cd);
             } else {
                 mNotificationHeader.setHeaderBackgroundDrawable(null);
@@ -755,6 +891,10 @@
     }
 
     public int getMaxContentHeight() {
+        if (showingAsLowPriority()) {
+            return getMinHeight(NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED, true
+                    /* likeHighPriority */);
+        }
         int maxContentHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
         int visibleChildren = 0;
         int childCount = mChildren.size();
@@ -781,13 +921,21 @@
         }
         mActualHeight = actualHeight;
         float fraction = getGroupExpandFraction();
+        boolean showingLowPriority = showingAsLowPriority();
+        updateHeaderTransformation();
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
         int childCount = mChildren.size();
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
-            float childHeight = child.isExpanded(true /* allowOnKeyguard */)
-                    ? child.getMaxExpandHeight()
-                    : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
+            float childHeight;
+            if (showingLowPriority) {
+                childHeight = child.getShowingLayout().getMinHeight(false /* likeGroupExpanded */);
+            } else if (child.isExpanded(true /* allowOnKeyguard */)) {
+                childHeight = child.getMaxExpandHeight();
+            } else {
+                childHeight = child.getShowingLayout().getMinHeight(
+                        true /* likeGroupExpanded */);
+            }
             if (i < maxAllowedVisibleChildren) {
                 float singleLineHeight = child.getShowingLayout().getMinHeight(
                         false /* likeGroupExpanded */);
@@ -800,7 +948,8 @@
     }
 
     public float getGroupExpandFraction() {
-        int visibleChildrenExpandedHeight = getVisibleChildrenExpandHeight();
+        int visibleChildrenExpandedHeight = showingAsLowPriority() ? getMaxContentHeight()
+                : getVisibleChildrenExpandHeight();
         int minExpandHeight = getCollapsedHeight();
         float factor = (mActualHeight - minExpandHeight)
                 / (float) (visibleChildrenExpandedHeight - minExpandHeight);
@@ -827,16 +976,23 @@
     }
 
     public int getMinHeight() {
-        return getMinHeight(NUMBER_OF_CHILDREN_WHEN_COLLAPSED);
+        return getMinHeight(NUMBER_OF_CHILDREN_WHEN_COLLAPSED, false /* likeHighPriority */);
     }
 
     public int getCollapsedHeight() {
-        return getMinHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */));
+        return getMinHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */),
+                false /* likeHighPriority */);
     }
 
-    private int getMinHeight(int maxAllowedVisibleChildren) {
-        if (mIsLowPriority && !mChildrenExpanded) {
-            return mNotificationHeader.getHeight();
+    /**
+     * Get the minimum Height for this group.
+     *
+     * @param maxAllowedVisibleChildren the number of children that should be visible
+     * @param likeHighPriority if the height should be calculated as if it were not low priority
+     */
+    private int getMinHeight(int maxAllowedVisibleChildren, boolean likeHighPriority) {
+        if (!likeHighPriority && showingAsLowPriority()) {
+            return mNotificationHeaderLowPriority.getHeight();
         }
         int minExpandHeight = mNotificationHeaderMargin;
         int visibleChildren = 0;
@@ -859,6 +1015,10 @@
         return minExpandHeight;
     }
 
+    public boolean showingAsLowPriority() {
+        return mIsLowPriority && !mContainingNotification.isExpanded();
+    }
+
     public void setDark(boolean dark, boolean fade, long delay) {
         if (mOverflowNumber != null) {
             mOverflowInvertHelper.setInverted(dark, fade, delay);
@@ -867,9 +1027,15 @@
     }
 
     public void reInflateViews(OnClickListener listener, StatusBarNotification notification) {
-        removeView(mNotificationHeader);
-        mNotificationHeader = null;
-        recreateNotificationHeader(listener, notification);
+        if (mNotificationHeader != null) {
+            removeView(mNotificationHeader);
+            mNotificationHeader = null;
+        }
+        if (mNotificationHeaderLowPriority != null) {
+            removeView(mNotificationHeaderLowPriority);
+            mNotificationHeaderLowPriority = null;
+        }
+        recreateNotificationHeader(listener);
         initDimens();
         for (int i = 0; i < mDividers.size(); i++) {
             View prevDivider = mDividers.get(i);
@@ -888,16 +1054,19 @@
 
     public void setUserLocked(boolean userLocked) {
         mUserLocked = userLocked;
+        if (!mUserLocked) {
+            updateHeaderVisibility(false /* animate */);
+        }
         int childCount = mChildren.size();
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
-            child.setUserLocked(userLocked);
+            child.setUserLocked(userLocked && !showingAsLowPriority());
         }
     }
 
     public void onNotificationUpdated() {
         mHybridGroupManager.setOverflowNumberColor(mOverflowNumber,
-                mNotificationParent.getNotificationColor());
+                mContainingNotification.getNotificationColor());
     }
 
     public int getPositionInLinearLayout(View childInGroup) {
@@ -926,6 +1095,13 @@
                 header.getIcon().setForceHidden(!iconsVisible);
             }
         }
+        if (mNotificationHeaderWrapperLowPriority != null) {
+            NotificationHeaderView header
+                    = mNotificationHeaderWrapperLowPriority.getNotificationHeader();
+            if (header != null) {
+                header.getIcon().setForceHidden(!iconsVisible);
+            }
+        }
     }
 
     public void setClipBottomAmount(int clipBottomAmount) {
@@ -935,5 +1111,36 @@
 
     public void setIsLowPriority(boolean isLowPriority) {
         mIsLowPriority = isLowPriority;
+        if (mContainingNotification != null) { /* we're not yet set up yet otherwise */
+            recreateLowPriorityHeader(null /* existingBuilder */);
+            updateHeaderVisibility(false /* animate */);
+        }
+        if (mUserLocked) {
+            setUserLocked(mUserLocked);
+        }
+    }
+
+    public NotificationHeaderView getVisibleHeader() {
+        NotificationHeaderView header = mNotificationHeader;
+        if (showingAsLowPriority()) {
+            header = mNotificationHeaderLowPriority;
+        }
+        return header;
+    }
+
+    public void onExpansionChanged() {
+        if (mIsLowPriority) {
+            if (mUserLocked) {
+                setUserLocked(mUserLocked);
+            }
+            updateHeaderVisibility(true /* animate */);
+        }
+    }
+
+    public float getIncreasedPaddingAmount() {
+        if (showingAsLowPriority()) {
+            return 0.0f;
+        }
+        return getGroupExpandFraction();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 48dbe49..d3b336b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3416,6 +3416,7 @@
         for (View view : mClearOverlayViewsWhenFinished) {
             StackStateAnimator.removeFromOverlay(view);
         }
+        mClearOverlayViewsWhenFinished.clear();
     }
 
     private void runAnimationFinishedRunnables() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index e0a6159..c8659fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -136,7 +136,7 @@
         yTranslation = view.getTranslationY();
         zTranslation = view.getTranslationZ();
         gone = view.getVisibility() == View.GONE;
-        hidden = false;
+        hidden = view.getVisibility() == View.INVISIBLE;
         scaleX = view.getScaleX();
         scaleY = view.getScaleY();
     }
@@ -594,7 +594,7 @@
     }
 
     protected void onYTranslationAnimationFinished(View view) {
-        if (hidden) {
+        if (hidden && !gone) {
             view.setVisibility(View.INVISIBLE);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 15ad0ce..5911766 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -36,19 +36,19 @@
         nm.createNotificationChannels(Arrays.asList(
                 new NotificationChannel(
                         ALERTS,
-                        context.getString(R.string.notification_channel_alerts),
+                        R.string.notification_channel_alerts,
                         NotificationManager.IMPORTANCE_HIGH),
                 new NotificationChannel(
                         SCREENSHOTS,
-                        context.getString(R.string.notification_channel_screenshot),
+                        R.string.notification_channel_screenshot,
                         NotificationManager.IMPORTANCE_LOW),
                 new NotificationChannel(
                         GENERAL,
-                        context.getString(R.string.notification_channel_general),
+                        R.string.notification_channel_general,
                         NotificationManager.IMPORTANCE_MIN),
                 new NotificationChannel(
                         STORAGE,
-                        context.getString(R.string.notification_channel_storage),
+                        R.string.notification_channel_storage,
                         NotificationManager.IMPORTANCE_LOW)
                 ));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java
index d07cea1d..f016aa1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java
@@ -52,11 +52,11 @@
         RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.custom_view_dark);
         View v = views.apply(mContext, null);
         NotificationViewWrapper wrap = NotificationCustomViewWrapper.wrap(mContext, v, mRow);
-        wrap.notifyContentUpdated(null, false /* isLowPriority */);
+        wrap.notifyContentUpdated(mRow);
         Assert.assertTrue(wrap.getCustomBackgroundColor() != 0);
         views.reapply(mContext, v);
-        wrap.notifyContentUpdated(null, false /* isLowPriority */);
+        wrap.notifyContentUpdated(mRow);
         Assert.assertTrue(wrap.getCustomBackgroundColor() != 0);
     }
 
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 0491fc4..92f8c7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -19,6 +19,8 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Matchers.anyObject;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
@@ -98,7 +100,6 @@
         mNotificationChannel = new NotificationChannel(
                 TEST_CHANNEL, TEST_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
         when(mMockStatusBarNotification.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
-
         when(mMockINotificationManager.getNumNotificationChannelsForPackage(
                 eq(TEST_PACKAGE_NAME), anyInt(), anyBoolean())).thenReturn(1);
     }
@@ -169,6 +170,28 @@
 
     @Test
     @UiThreadTest
+    public void testBindNotification_SetsGroupName_resId() throws Exception {
+        when(mMockPackageManager.getText(eq(TEST_PACKAGE_NAME),
+                eq(R.string.legacy_vpn_name), anyObject())).thenReturn(
+                getContext().getString(R.string.legacy_vpn_name));
+        mNotificationChannel.setGroup("test_group_id");
+        final NotificationChannelGroup notificationChannelGroup =
+                new NotificationChannelGroup("test_group_id", R.string.legacy_vpn_name);
+        when(mMockINotificationManager.getNotificationChannelGroupForPackage(
+                eq("test_group_id"), eq(TEST_PACKAGE_NAME), anyInt()))
+                .thenReturn(notificationChannelGroup);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, mNotificationChannel, null, null, null);
+        final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name);
+        assertEquals(View.VISIBLE, groupNameView.getVisibility());
+        assertEquals(mContext.getString(R.string.legacy_vpn_name), groupNameView.getText());
+        final TextView groupDividerView =
+                (TextView) mNotificationInfo.findViewById(R.id.pkg_group_divider);
+        assertEquals(View.VISIBLE, groupDividerView.getVisibility());
+    }
+
+    @Test
+    @UiThreadTest
     public void testBindNotification_SetsTextChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 mMockStatusBarNotification, mNotificationChannel, null, null, null);
@@ -178,6 +201,22 @@
 
     @Test
     @UiThreadTest
+    public void testBindNotification_SetsTextChannelName_resId() throws Exception {
+        when(mMockPackageManager.getText(eq(TEST_PACKAGE_NAME),
+                eq(R.string.notification_menu_accessibility), anyObject())).thenReturn(
+                getContext().getString(R.string.notification_menu_accessibility));
+        NotificationChannel notificationChannelResId = new NotificationChannel(
+                TEST_CHANNEL, R.string.notification_menu_accessibility,
+                NotificationManager.IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                mMockStatusBarNotification, notificationChannelResId, null, null, null);
+        final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
+        assertEquals(getContext().getString(R.string.notification_menu_accessibility),
+                textView.getText());
+    }
+
+    @Test
+    @UiThreadTest
     public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
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 ba20999..e47f750 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
@@ -12,6 +12,7 @@
 
 import com.android.settingslib.net.DataUsageController;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -106,6 +107,7 @@
                 TelephonyIcons.QS_DATA_4G);
     }
 
+    @Ignore("Flaky")
     @Test
     public void testDataDisabledIcon() {
         setupNetworkController();
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 2c0f9c9..1555856 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
@@ -94,6 +94,14 @@
     }
 
     @Test
+    public void testNoEmergencyOnlyWrongSubscription() {
+        setupDefaultSignal();
+        setDefaultSubId(42);
+        mNetworkController.recalculateEmergency();
+        verifyEmergencyOnly(false);
+    }
+
+    @Test
     public void testNoEmengencyNoSubscriptions() {
         setupDefaultSignal();
         setSubscriptions();
@@ -286,12 +294,12 @@
         for (int i = 0; i < testSubscriptions.length; i++) {
             if (i == indexToSkipController) {
                 // Make sure a controller was created despite us not adding one.
-                assertTrue(mNetworkController.mMobileSignalControllers.containsKey(
-                        testSubscriptions[i]));
+                assertTrue(mNetworkController.mMobileSignalControllers.indexOfKey(
+                        testSubscriptions[i]) >= 0);
             } else if (i == indexToSkipSubscription) {
                 // Make sure the controller that did exist was removed
-                assertFalse(mNetworkController.mMobileSignalControllers.containsKey(
-                        testSubscriptions[i]));
+                assertFalse(mNetworkController.mMobileSignalControllers.indexOfKey(
+                        testSubscriptions[i]) >= 0);
             } else {
                 // If a MobileSignalController is around it needs to not be unregistered.
                 Mockito.verify(mobileSignalControllers[i], Mockito.never())
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 4bacb58..2b219e6 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2157,7 +2157,7 @@
 
     // ACTION: Settings -> Select summary tab.
     // CATEGORY: SETTINGS
-    ACTION_SELECT_SUMMARY=476;
+    ACTION_SELECT_SUMMARY = 476;
 
     // ACTION: Settings -> Select support tab.
     // CATEGORY: SETTINGS
@@ -2208,7 +2208,7 @@
     NIGHT_DISPLAY_SETTINGS = 488;
 
     // ACTION: Settings -> Storage -> Manage storage -> Click Storage Manager
-        //   SUBTYPE: false is off, true is on
+    //   SUBTYPE: false is off, true is on
     ACTION_TOGGLE_STORAGE_MANAGER = 489;
 
     // Settings launched from collapsed quick settings.
@@ -3477,6 +3477,15 @@
     // OS: O
     WIFI_NETWORK_DETAILS = 849;
 
+    // ACTION: Settings > Battery > Menu > Usage Alerts
+    ACTION_SETTINGS_MENU_BATTERY_USAGE_ALERTS = 850;
+
+    // ACTION: Settings > Battery > Menu > Optimization
+    ACTION_SETTINGS_MENU_BATTERY_OPTIMIZATION = 851;
+
+    // ACTION: Settings > Battery > Menu > Apps Toggle
+    ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE = 852;
+
     // ---- End O Constants, all O constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index 7a85d4e..8947b7d 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -186,6 +186,29 @@
     }
 
     // Called by Shell command.
+    void destroySessions(int userId, IResultReceiver receiver) {
+        Slog.i(TAG, "destroySessions() for userId " + userId);
+        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+
+        synchronized (mLock) {
+            if (userId != UserHandle.USER_ALL) {
+                mServicesCache.get(userId).destroySessionsLocked();
+            } else {
+                final int size = mServicesCache.size();
+                for (int i = 0; i < size; i++) {
+                    mServicesCache.valueAt(i).destroySessionsLocked();
+                }
+            }
+        }
+
+        try {
+            receiver.send(0, new Bundle());
+        } catch (RemoteException e) {
+            // Just ignore it...
+        }
+    }
+
+    // Called by Shell command.
     void listSessions(int userId, IResultReceiver receiver) {
         Slog.i(TAG, "listSessions() for userId " + userId);
         mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index bfc6e83..5e852f1 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -59,7 +59,6 @@
 import android.service.autofill.SaveInfo;
 import android.text.TextUtils;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
@@ -405,6 +404,12 @@
         }
     }
 
+    void destroySessionsLocked() {
+        for (Session session : mSessions.values()) {
+            session.removeSelf();
+        }
+    }
+
     void listSessionsLocked(ArrayList<String> output) {
         for (IBinder activityToken : mSessions.keySet()) {
             output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName()
@@ -666,6 +671,28 @@
 
         // FillServiceCallbacks
         @Override
+        public void onDisableSelf() {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                final String autoFillService = Settings.Secure.getStringForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.AUTO_FILL_SERVICE, mUserId);
+                if (mInfo.getServiceInfo().getComponentName().equals(
+                        ComponentName.unflattenFromString(autoFillService))) {
+                    Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                            Settings.Secure.AUTO_FILL_SERVICE, null, mUserId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            synchronized (mLock) {
+                destroyLocked();
+                mSessions.remove(this);
+            }
+        }
+
+        // FillServiceCallbacks
+        @Override
         public void onServiceDied(RemoteFillService service) {
             // TODO(b/33197203): implement
         }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
index cfa4a1d..76eed2a 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
@@ -20,7 +20,6 @@
 
 import android.app.ActivityManager;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.ShellCommand;
 import android.os.UserHandle;
 
@@ -50,6 +49,8 @@
                 return requestSave();
             case "list":
                 return requestList(pw);
+            case "destroy":
+                return requestDestroy(pw);
             case "reset":
                 return requestReset();
             default:
@@ -67,6 +68,9 @@
             pw.println("  list sessions [--user USER_ID]");
             pw.println("    List all pending sessions.");
             pw.println("");
+            pw.println("  destroy sessions [--user USER_ID]");
+            pw.println("    Destroy all pending sessions.");
+            pw.println("");
             pw.println("  save [--user USER_ID]");
             pw.println("    Request provider to save contents of the top activity. ");
             pw.println("");
@@ -82,30 +86,55 @@
         return 0;
     }
 
-    private int requestList(PrintWriter pw) {
-        final String type = getNextArgRequired();
-        if (!type.equals("sessions")) {
-            pw.println("Error: invalid list type");
+    private int requestDestroy(PrintWriter pw) {
+        if (!isNextArgSessions(pw)) {
             return -1;
-
         }
+
         final int userId = getUserIdFromArgsOrAllUsers();
         final CountDownLatch latch = new CountDownLatch(1);
         final IResultReceiver receiver = new IResultReceiver.Stub() {
-
             @Override
-            public void send(int resultCode, Bundle resultData) throws RemoteException {
+            public void send(int resultCode, Bundle resultData) {
+                latch.countDown();
+            }
+        };
+        return requestSessionCommon(pw, latch, () -> mService.destroySessions(userId, receiver));
+    }
+
+    private int requestList(PrintWriter pw) {
+        if (!isNextArgSessions(pw)) {
+            return -1;
+        }
+
+        final int userId = getUserIdFromArgsOrAllUsers();
+        final CountDownLatch latch = new CountDownLatch(1);
+        final IResultReceiver receiver = new IResultReceiver.Stub() {
+            @Override
+            public void send(int resultCode, Bundle resultData) {
                 final ArrayList<String> sessions = resultData
                         .getStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS);
-
                 for (String session : sessions) {
                     pw.println(session);
                 }
                 latch.countDown();
             }
         };
+        return requestSessionCommon(pw, latch, () -> mService.listSessions(userId, receiver));
+    }
 
-        mService.listSessions(userId, receiver);
+    private boolean isNextArgSessions(PrintWriter pw) {
+        final String type = getNextArgRequired();
+        if (!type.equals("sessions")) {
+            pw.println("Error: invalid list type");
+            return false;
+        }
+        return true;
+    }
+
+    private int requestSessionCommon(PrintWriter pw, CountDownLatch latch,
+            Runnable command) {
+        command.run();
 
         try {
             final boolean received = latch.await(5, TimeUnit.SECONDS);
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index c469718..04eeb32 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -33,6 +33,7 @@
 import android.service.autofill.AutoFillService;
 import android.service.autofill.FillResponse;
 import android.service.autofill.IAutoFillService;
+import android.service.autofill.IAutoFillServiceConnection;
 import android.service.autofill.IFillCallback;
 import android.service.autofill.ISaveCallback;
 import android.text.format.DateUtils;
@@ -57,7 +58,7 @@
     private static final boolean DEBUG = Helper.DEBUG;
 
     // How long after the last interaction with the service we would unbind
-    private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.MINUTE_IN_MILLIS;
+    private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
 
     private final Context mContext;
 
@@ -91,6 +92,7 @@
         void onSaveRequestSuccess();
         void onSaveRequestFailure(CharSequence message);
         void onServiceDied(RemoteFillService service);
+        void onDisableSelf();
     }
 
     public RemoteFillService(Context context, ComponentName componentName,
@@ -197,6 +199,10 @@
         }
     }
 
+    private void handleDisableSelf() {
+        mCallbacks.onDisableSelf();
+    }
+
     private boolean isBound() {
         return mAutoFillService != null;
     }
@@ -239,7 +245,7 @@
             // Need to double check if it's null, since it could be set on onServiceDisconnected()
             if (mAutoFillService != null) {
                 try {
-                    mAutoFillService.onDisconnected();
+                    mAutoFillService.onInit(null);
                 } catch (Exception e) {
                     Slog.w(LOG_TAG, "Exception calling onDisconnected(): " + e);
                 }
@@ -321,7 +327,12 @@
                 // Need to double check if it's null, since it could be set on
                 // onServiceDisconnected()
                 if (mAutoFillService != null) {
-                    mAutoFillService.onConnected();
+                    mAutoFillService.onInit(new IAutoFillServiceConnection.Stub() {
+                        @Override
+                        public void disableSelf() {
+                            mHandler.obtainMessage(MyHandler.MSG_ON_DISABLE_SELF).sendToTarget();
+                        }
+                    });
                 }
             } catch (RemoteException e) {
                 Slog.w(LOG_TAG, "Exception calling onConnected(): " + e);
@@ -347,6 +358,7 @@
         public static final int MSG_BINDER_DIED = 2;
         public static final int MSG_UNBIND = 3;
         public static final int MSG_ON_PENDING_REQUEST = 4;
+        public static final int MSG_ON_DISABLE_SELF = 5;
 
         public MyHandler(Context context) {
             // Cannot use lambda - doesn't compile
@@ -374,6 +386,10 @@
                         case MSG_ON_PENDING_REQUEST: {
                             handlePendingRequest((PendingRequest) message.obj);
                         } break;
+
+                        case MSG_ON_DISABLE_SELF: {
+                            handleDisableSelf();
+                        } break;
                     }
                 }
             }, false);
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 949a80c..0da36c8 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -26,6 +26,7 @@
 import android.service.autofill.FillResponse;
 import android.service.autofill.SaveInfo;
 import android.text.TextUtils;
+import android.util.Slog;
 import android.view.autofill.AutoFillId;
 import android.widget.Toast;
 
@@ -41,6 +42,8 @@
  * managing saving of user edits.
  */
 public final class AutoFillUI {
+    private static final String TAG = "AutoFillUI";
+
     private final Handler mHandler = UiThread.getHandler();
     private final @NonNull Context mContext;
 
@@ -191,9 +194,17 @@
                 }
 
                 @Override
-                public void onCancel() {
+                public void onCancel(IntentSender listener) {
                     // TODO(b/33197203): add MetricsLogger call
                     hideSaveUiUiThread();
+                    if (listener != null) {
+                        try {
+                            listener.sendIntent(mContext, 0, null, null, null);
+                        } catch (IntentSender.SendIntentException e) {
+                            Slog.e(TAG, "Error starting negative action listener: "
+                                    + listener, e);
+                        }
+                    }
                 }
             });
         });
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index afe93c7..d443dc3 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.app.Dialog;
 import android.content.Context;
+import android.content.IntentSender;
 import android.os.Handler;
 import android.service.autofill.SaveInfo;
 import android.text.format.DateUtils;
@@ -38,7 +39,7 @@
 final class SaveUi {
     public interface OnSaveListener {
         void onSave();
-        void onCancel();
+        void onCancel(IntentSender listener);
     }
 
     private static final long LIFETIME_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
@@ -87,14 +88,20 @@
             subTitleView.setVisibility(View.VISIBLE);
         }
 
-        final View noButton = view.findViewById(R.id.autofill_save_no);
-        noButton.setOnClickListener((v) -> mListener.onCancel());
+        final TextView noButton = view.findViewById(R.id.autofill_save_no);
+        if (info.getNegativeActionTitle() != null) {
+            noButton.setText(info.getNegativeActionTitle());
+            noButton.setOnClickListener((v) -> mListener.onCancel(
+                    info.getNegativeActionListener()));
+        } else {
+            noButton.setOnClickListener((v) -> mListener.onCancel(null));
+        }
 
         final View yesButton = view.findViewById(R.id.autofill_save_yes);
         yesButton.setOnClickListener((v) -> mListener.onSave());
 
         final View closeButton = view.findViewById(R.id.autofill_save_close);
-        closeButton.setOnClickListener((v) -> mListener.onCancel());
+        closeButton.setOnClickListener((v) -> mListener.onCancel(null));
 
         mDialog = new Dialog(context, R.style.Theme_Material_Panel);
         mDialog.setContentView(view);
@@ -112,7 +119,7 @@
 
         mDialog.show();
 
-        mHandler.postDelayed(() -> mListener.onCancel(), LIFETIME_MILLIS);
+        mHandler.postDelayed(() -> mListener.onCancel(null), LIFETIME_MILLIS);
     }
 
     void destroy() {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 4f8b8af..2d947a4 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -16,9 +16,23 @@
 
 package com.android.server.backup;
 
-import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER;
-import static android.app.backup.BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY;
+import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME;
+import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION;
 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_OLD_VERSION;
+import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_POLICY_ALLOW_APKS;
+import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_MANIFEST_PACKAGE_NAME;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_SYSTEM_APP_NO_AGENT;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_APK_NOT_INSTALLED;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_MISSING_SIGNATURE;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_RESTORE_ANY_VERSION;
+import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
 
 import android.app.ActivityManager;
@@ -2374,6 +2388,8 @@
         IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
         if (transport == null) {
             sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
+            monitor = monitorEvent(monitor, BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
+                    null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
             return BackupManager.ERROR_TRANSPORT_ABORTED;
         }
 
@@ -3338,6 +3354,14 @@
                                     addBackupTrace("illegal key " + key + " from " + pkgName);
                                     EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName,
                                             "bad key");
+                                    mMonitor = monitorEvent(mMonitor,
+                                            BackupManagerMonitor.LOG_EVENT_ID_ILLEGAL_KEY,
+                                            mCurrentPackage,
+                                            BackupManagerMonitor
+                                                    .LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                            putMonitoringExtra(null,
+                                                    BackupManagerMonitor.EXTRA_LOG_ILLEGAL_KEY,
+                                                    key));
                                     mBackupHandler.removeMessages(MSG_BACKUP_OPERATION_TIMEOUT);
                                     sendBackupOnPackageResult(mObserver, pkgName,
                                             BackupManager.ERROR_AGENT_FAILURE);
@@ -3407,6 +3431,11 @@
                         if (MORE_DEBUG) Slog.i(TAG,
                                 "no backup data written; not calling transport");
                         addBackupTrace("no data to send");
+                        mMonitor = monitorEvent(mMonitor,
+                                BackupManagerMonitor.LOG_EVENT_ID_NO_DATA_TO_SEND,
+                                mCurrentPackage,
+                                BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                null);
                     }
 
                     if (mStatus == BackupTransport.TRANSPORT_OK) {
@@ -3502,8 +3531,10 @@
                 Slog.e(TAG, "Cancel backing up " + mCurrentPackage.packageName);
                 EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName);
                 mMonitor = monitorEvent(mMonitor,
-                    BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_TIMEOUT,
-                    mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null);
+                        BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_BACKUP_CANCEL,
+                        mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+                        putMonitoringExtra(null, BackupManagerMonitor.EXTRA_LOG_CANCEL_ALL,
+                                mCancelAll));
                 addBackupTrace(
                         "cancel of " + mCurrentPackage.packageName + ", cancelAll=" + cancelAll);
                 errorCleanup();
@@ -4593,6 +4624,11 @@
                         if (MORE_DEBUG) {
                             Slog.d(TAG, "Ignoring ineligible package " + pkg);
                         }
+                        mMonitor = monitorEvent(mMonitor,
+                                BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_INELIGIBLE,
+                                mCurrentPackage,
+                                BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                null);
                         sendBackupOnPackageResult(mBackupObserver, pkg,
                             BackupManager.ERROR_BACKUP_NOT_ALLOWED);
                         continue;
@@ -4603,6 +4639,11 @@
                             Slog.d(TAG, "Ignoring full-data backup of key/value participant "
                                     + pkg);
                         }
+                        mMonitor = monitorEvent(mMonitor,
+                                BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_KEY_VALUE_PARTICIPANT,
+                                mCurrentPackage,
+                                BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                null);
                         sendBackupOnPackageResult(mBackupObserver, pkg,
                                 BackupManager.ERROR_BACKUP_NOT_ALLOWED);
                         continue;
@@ -4613,6 +4654,11 @@
                         if (MORE_DEBUG) {
                             Slog.d(TAG, "Ignoring stopped package " + pkg);
                         }
+                        mMonitor = monitorEvent(mMonitor,
+                                BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_STOPPED,
+                                mCurrentPackage,
+                                BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                null);
                         sendBackupOnPackageResult(mBackupObserver, pkg,
                                 BackupManager.ERROR_BACKUP_NOT_ALLOWED);
                         continue;
@@ -4620,7 +4666,7 @@
                     mPackages.add(info);
                 } catch (NameNotFoundException e) {
                     Slog.i(TAG, "Requested package " + pkg + " not found; ignoring");
-                    monitor = monitorEvent(monitor,
+                    mMonitor = monitorEvent(mMonitor,
                             BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_NOT_FOUND,
                             mCurrentPackage,
                             BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
@@ -4696,9 +4742,17 @@
                 if (!mEnabled || !mProvisioned) {
                     // Backups are globally disabled, so don't proceed.
                     if (DEBUG) {
-                        Slog.i(TAG, "full backup requested but e=" + mEnabled
-                                + " p=" + mProvisioned + "; ignoring");
+                        Slog.i(TAG, "full backup requested but enabled=" + mEnabled
+                                + " provisioned=" + mProvisioned + "; ignoring");
                     }
+                    int monitoringEvent;
+                    if (!mEnabled) {
+                        monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED;
+                    } else {
+                        monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
+                    }
+                    mMonitor = monitorEvent(mMonitor, monitoringEvent, null,
+                            BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
                     mUpdateSchedule = false;
                     backupRunStatus = BackupManager.ERROR_BACKUP_NOT_ALLOWED;
                     return;
@@ -4722,7 +4776,8 @@
                     PackageInfo currentPackage = mPackages.get(i);
                     String packageName = currentPackage.packageName;
                     if (DEBUG) {
-                        Slog.i(TAG, "Initiating full-data transport backup of " + packageName);
+                        Slog.i(TAG, "Initiating full-data transport backup of " + packageName
+                                + " token: " + mCurrentOpToken);
                     }
                     EventLog.writeEvent(EventLogTags.FULL_BACKUP_PACKAGE, packageName);
 
@@ -4781,6 +4836,13 @@
                                         + packageName + ": " + preflightResult
                                         + ", not running backup.");
                             }
+                            mMonitor = monitorEvent(mMonitor,
+                                    BackupManagerMonitor.LOG_EVENT_ID_ERROR_PREFLIGHT,
+                                    mCurrentPackage,
+                                    BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                    putMonitoringExtra(null,
+                                            BackupManagerMonitor.EXTRA_LOG_PREFLIGHT_ERROR,
+                                            preflightResult));
                             backupPackageStatus = (int) preflightResult;
                         } else {
                             int nRead = 0;
@@ -4808,6 +4870,11 @@
                             if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
                                 Slog.w(TAG, "Package hit quota limit in-flight " + packageName
                                         + ": " + totalRead + " of " + quota);
+                                mMonitor = monitorEvent(mMonitor,
+                                        BackupManagerMonitor.LOG_EVENT_ID_QUOTA_HIT_PREFLIGHT,
+                                        mCurrentPackage,
+                                        BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+                                        null);
                                 mBackupRunner.sendQuotaExceeded(totalRead, quota);
                             }
                         }
@@ -4942,6 +5009,14 @@
             } catch (Exception e) {
                 backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
                 Slog.w(TAG, "Exception trying full transport backup", e);
+                mMonitor = monitorEvent(mMonitor,
+                        BackupManagerMonitor.LOG_EVENT_ID_EXCEPTION_FULL_BACKUP,
+                        mCurrentPackage,
+                        BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                        putMonitoringExtra(null,
+                                BackupManagerMonitor.EXTRA_LOG_EXCEPTION_FULL_BACKUP,
+                                Log.getStackTraceString(e)));
+
             } finally {
 
                 if (mCancelAll) {
@@ -5221,7 +5296,7 @@
                 }
 
                 mMonitor = monitorEvent(mMonitor,
-                        BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_TIMEOUT,
+                        BackupManagerMonitor.LOG_EVENT_ID_FULL_BACKUP_CANCEL,
                         mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null);
                 mIsCancelled = true;
                 // Cancel tasks spun off by this task.
@@ -6309,9 +6384,29 @@
                 } else {
                     Slog.w(TAG, "Metadata mismatch: package " + info.packageName
                             + " but widget data for " + pkg);
+
+                    Bundle monitoringExtras = putMonitoringExtra(null,
+                            EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
+                    monitoringExtras = putMonitoringExtra(monitoringExtras,
+                            BackupManagerMonitor.EXTRA_LOG_WIDGET_PACKAGE_NAME, pkg);
+                    mMonitor = monitorEvent(mMonitor,
+                            BackupManagerMonitor.LOG_EVENT_ID_WIDGET_METADATA_MISMATCH,
+                            null,
+                            LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                            monitoringExtras);
                 }
             } else {
                 Slog.w(TAG, "Unsupported metadata version " + version);
+
+                Bundle monitoringExtras = putMonitoringExtra(null, EXTRA_LOG_EVENT_PACKAGE_NAME,
+                        info.packageName);
+                monitoringExtras = putMonitoringExtra(monitoringExtras,
+                        EXTRA_LOG_EVENT_PACKAGE_VERSION, version);
+                mMonitor = monitorEvent(mMonitor,
+                        BackupManagerMonitor.LOG_EVENT_ID_WIDGET_UNKNOWN_VERSION,
+                        null,
+                        LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                        monitoringExtras);
             }
         }
 
@@ -6386,10 +6481,20 @@
                                             if ((pkgInfo.applicationInfo.flags
                                                     & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) {
                                                 Slog.i(TAG, "Package has restoreAnyVersion; taking data");
+                                                mMonitor = monitorEvent(mMonitor,
+                                                        LOG_EVENT_ID_RESTORE_ANY_VERSION,
+                                                        pkgInfo,
+                                                        LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                                        null);
                                                 policy = RestorePolicy.ACCEPT;
                                             } else if (pkgInfo.versionCode >= version) {
                                                 Slog.i(TAG, "Sig + version match; taking data");
                                                 policy = RestorePolicy.ACCEPT;
+                                                mMonitor = monitorEvent(mMonitor,
+                                                        LOG_EVENT_ID_VERSIONS_MATCH,
+                                                        pkgInfo,
+                                                        LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                                        null);
                                             } else {
                                                 // The data is from a newer version of the app than
                                                 // is presently installed.  That means we can only
@@ -6403,30 +6508,43 @@
                                                 } else {
                                                     Slog.i(TAG, "Data requires newer version "
                                                             + version + "; ignoring");
-                                                    ArrayList<Pair<String, String>> list =
-                                                            new ArrayList<>();
-                                                    list.add(new Pair<String, String>(
-                                                            EXTRA_LOG_OLD_VERSION,
-                                                            Integer.toString(version)));
                                                     mMonitor = monitorEvent(mMonitor,
                                                             LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER,
                                                             pkgInfo,
                                                             LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
-                                                            list);
+                                                            putMonitoringExtra(null,
+                                                                    EXTRA_LOG_OLD_VERSION,
+                                                                    version));
+
                                                     policy = RestorePolicy.IGNORE;
                                                 }
                                             }
                                         } else {
                                             Slog.w(TAG, "Restore manifest signatures do not match "
                                                     + "installed application for " + info.packageName);
+                                            mMonitor = monitorEvent(mMonitor,
+                                                    LOG_EVENT_ID_FULL_RESTORE_SIGNATURE_MISMATCH,
+                                                    pkgInfo,
+                                                    LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                                    null);
                                         }
                                     } else {
                                         Slog.w(TAG, "Package " + info.packageName
                                                 + " is system level with no agent");
+                                        mMonitor = monitorEvent(mMonitor,
+                                                LOG_EVENT_ID_SYSTEM_APP_NO_AGENT,
+                                                pkgInfo,
+                                                LOG_EVENT_CATEGORY_AGENT,
+                                                null);
                                     }
                                 } else {
                                     if (DEBUG) Slog.i(TAG, "Restore manifest from "
                                             + info.packageName + " but allowBackup=false");
+                                    mMonitor = monitorEvent(mMonitor,
+                                            LOG_EVENT_ID_FULL_RESTORE_ALLOW_BACKUP_FALSE,
+                                            pkgInfo,
+                                            LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                            null);
                                 }
                             } catch (NameNotFoundException e) {
                                 // Okay, the target app isn't installed.  We can process
@@ -6439,26 +6557,71 @@
                                 } else {
                                     policy = RestorePolicy.IGNORE;
                                 }
+                                Bundle monitoringExtras = putMonitoringExtra(null,
+                                        EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
+                                monitoringExtras = putMonitoringExtra(monitoringExtras,
+                                        EXTRA_LOG_POLICY_ALLOW_APKS, mAllowApks);
+                                mMonitor = monitorEvent(mMonitor,
+                                        LOG_EVENT_ID_APK_NOT_INSTALLED,
+                                        null,
+                                        LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                        monitoringExtras);
                             }
 
                             if (policy == RestorePolicy.ACCEPT_IF_APK && !hasApk) {
                                 Slog.i(TAG, "Cannot restore package " + info.packageName
                                         + " without the matching .apk");
+                                mMonitor = monitorEvent(mMonitor,
+                                        LOG_EVENT_ID_CANNOT_RESTORE_WITHOUT_APK,
+                                        null,
+                                        LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                        putMonitoringExtra(null,
+                                                EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName));
                             }
                         } else {
                             Slog.i(TAG, "Missing signature on backed-up package "
                                     + info.packageName);
+                            mMonitor = monitorEvent(mMonitor,
+                                    LOG_EVENT_ID_MISSING_SIGNATURE,
+                                    null,
+                                    LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                    putMonitoringExtra(null,
+                                            EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName));
                         }
                     } else {
                         Slog.i(TAG, "Expected package " + info.packageName
                                 + " but restore manifest claims " + manifestPackage);
+                        Bundle monitoringExtras = putMonitoringExtra(null,
+                                EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
+                        monitoringExtras = putMonitoringExtra(monitoringExtras,
+                                EXTRA_LOG_MANIFEST_PACKAGE_NAME, manifestPackage);
+                        mMonitor = monitorEvent(mMonitor,
+                                LOG_EVENT_ID_EXPECTED_DIFFERENT_PACKAGE,
+                                null,
+                                LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                monitoringExtras);
                     }
                 } else {
                     Slog.i(TAG, "Unknown restore manifest version " + version
                             + " for package " + info.packageName);
+                    Bundle monitoringExtras = putMonitoringExtra(null,
+                            EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName);
+                    monitoringExtras = putMonitoringExtra(monitoringExtras,
+                            EXTRA_LOG_EVENT_PACKAGE_VERSION, version);
+                    mMonitor = monitorEvent(mMonitor,
+                            BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_VERSION,
+                            null,
+                            LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                            monitoringExtras);
+
                 }
             } catch (NumberFormatException e) {
                 Slog.w(TAG, "Corrupt restore manifest for package " + info.packageName);
+                mMonitor = monitorEvent(mMonitor,
+                        BackupManagerMonitor.LOG_EVENT_ID_CORRUPT_MANIFEST,
+                        null,
+                        LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                        putMonitoringExtra(null, EXTRA_LOG_EVENT_PACKAGE_NAME, info.packageName));
             } catch (IllegalArgumentException e) {
                 Slog.w(TAG, e.getMessage());
             }
@@ -8557,7 +8720,12 @@
                     return;
                 }
                 if (!PACKAGE_MANAGER_SENTINEL.equals(desc.getPackageName())) {
-                    Slog.e(TAG, "Required metadata but got " + desc.getPackageName());
+                    Slog.e(TAG, "Required package metadata but got "
+                            + desc.getPackageName());
+                    mMonitor = monitorEvent(mMonitor,
+                            BackupManagerMonitor.LOG_EVENT_ID_NO_PM_METADATA_RECEIVED,
+                            mCurrentPackage,
+                            BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
                     mStatus = BackupTransport.TRANSPORT_ERROR;
                     executeNextState(UnifiedRestoreState.FINAL);
                     return;
@@ -8585,7 +8753,11 @@
                 // signature/version verification etc, so we simply do not proceed with
                 // the restore operation.
                 if (!mPmAgent.hasMetadata()) {
-                    Slog.e(TAG, "No restore metadata available, so not restoring");
+                    Slog.e(TAG, "PM agent has no metadata, so not restoring");
+                    mMonitor = monitorEvent(mMonitor,
+                            BackupManagerMonitor.LOG_EVENT_ID_PM_AGENT_HAS_NO_METADATA,
+                            mCurrentPackage,
+                            BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
                     EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
                             PACKAGE_MANAGER_SENTINEL,
                             "Package manager restore metadata missing");
@@ -8601,6 +8773,10 @@
             } catch (Exception e) {
                 // If we lost the transport at any time, halt
                 Slog.e(TAG, "Unable to contact transport for restore: " + e.getMessage());
+                mMonitor = monitorEvent(mMonitor,
+                        BackupManagerMonitor.LOG_EVENT_ID_LOST_TRANSPORT,
+                        null,
+                        BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
                 mStatus = BackupTransport.TRANSPORT_ERROR;
                 mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
                 executeNextState(UnifiedRestoreState.FINAL);
@@ -8670,17 +8846,37 @@
                     // handle this case, we do not attempt the restore.
                     if ((mCurrentPackage.applicationInfo.flags
                             & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
-                        String message = "Version " + metaInfo.versionCode
+                        String message = "Source version " + metaInfo.versionCode
                                 + " > installed version " + mCurrentPackage.versionCode;
                         Slog.w(TAG, "Package " + pkgName + ": " + message);
+                        Bundle monitoringExtras = putMonitoringExtra(null,
+                                BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
+                                metaInfo.versionCode);
+                        monitoringExtras = putMonitoringExtra(monitoringExtras,
+                                BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY, false);
+                        mMonitor = monitorEvent(mMonitor,
+                                BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
+                                mCurrentPackage,
+                                BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                monitoringExtras);
                         EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
                                 pkgName, message);
                         nextState = UnifiedRestoreState.RUNNING_QUEUE;
                         return;
                     } else {
-                        if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
-                                + " > installed " + mCurrentPackage.versionCode
+                        if (DEBUG) Slog.v(TAG, "Source version " + metaInfo.versionCode
+                                + " > installed version " + mCurrentPackage.versionCode
                                 + " but restoreAnyVersion");
+                        Bundle monitoringExtras = putMonitoringExtra(null,
+                                BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
+                                metaInfo.versionCode);
+                        monitoringExtras = putMonitoringExtra(monitoringExtras,
+                                BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY, true);
+                        mMonitor = monitorEvent(mMonitor,
+                                BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
+                                mCurrentPackage,
+                                BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+                                monitoringExtras);
                     }
                 }
 
@@ -8739,6 +8935,9 @@
             Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
             if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
                 Slog.w(TAG, "Signature mismatch restoring " + packageName);
+                mMonitor = monitorEvent(mMonitor,
+                        BackupManagerMonitor.LOG_EVENT_ID_SIGNATURE_MISMATCH, mCurrentPackage,
+                        BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
                         "Signature mismatch");
                 executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
@@ -11035,23 +11234,54 @@
         }
     }
 
+    private Bundle putMonitoringExtra(Bundle extras, String key, String value) {
+        if (extras == null) {
+            extras = new Bundle();
+        }
+        extras.putString(key, value);
+        return extras;
+    }
+
+    private Bundle putMonitoringExtra(Bundle extras, String key, int value) {
+        if (extras == null) {
+            extras = new Bundle();
+        }
+        extras.putInt(key, value);
+        return extras;
+    }
+
+    private Bundle putMonitoringExtra(Bundle extras, String key, long value) {
+        if (extras == null) {
+            extras = new Bundle();
+        }
+        extras.putLong(key, value);
+        return extras;
+    }
+
+
+    private Bundle putMonitoringExtra(Bundle extras, String key, boolean value) {
+        if (extras == null) {
+            extras = new Bundle();
+        }
+        extras.putBoolean(key, value);
+        return extras;
+    }
+
     private static IBackupManagerMonitor monitorEvent(IBackupManagerMonitor monitor, int id,
-            PackageInfo pkg, int category, ArrayList<Pair<String, String>> extras) {
+            PackageInfo pkg, int category, Bundle extras) {
         if (monitor != null) {
             try {
                 Bundle bundle = new Bundle();
                 bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, id);
                 bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY, category);
                 if (pkg != null) {
-                    bundle.putString(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME,
+                    bundle.putString(EXTRA_LOG_EVENT_PACKAGE_NAME,
                             pkg.packageName);
                     bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION,
                             pkg.versionCode);
                 }
                 if (extras != null) {
-                    for (Pair<String,String> pair : extras) {
-                        bundle.putString(pair.first, pair.second);
-                    }
+                    bundle.putAll(extras);
                 }
                 monitor.onEvent(bundle);
                 return monitor;
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index b9dbe13..619ddb1 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -41,6 +41,7 @@
 import com.android.server.EventLogTags;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -103,13 +104,18 @@
         // been removed from mBoundTransports because onServiceDisconnected would already been
         // called on TransportConnection objects.
         synchronized (mTransportLock) {
-            for (ComponentName transport : mValidTransports.keySet()) {
-                if (transport.getPackageName().equals(packageName)) {
-                    TransportConnection removed = mValidTransports.remove(transport);
-                    if (removed != null) {
-                        mContext.unbindService(removed);
-                        log_verbose("Package removed, Removing transport: " +
-                                transport.flattenToShortString());
+            Iterator<Map.Entry<ComponentName, TransportConnection>> iter =
+                    mValidTransports.entrySet().iterator();
+            while (iter.hasNext()) {
+                Map.Entry<ComponentName, TransportConnection> validTransport = iter.next();
+                ComponentName componentName = validTransport.getKey();
+                if (componentName.getPackageName().equals(packageName)) {
+                    TransportConnection transportConnection = validTransport.getValue();
+                    iter.remove();
+                    if (transportConnection != null) {
+                        mContext.unbindService(transportConnection);
+                        log_verbose("Package removed, removing transport: "
+                                + componentName.flattenToShortString());
                     }
                 }
             }
@@ -278,15 +284,16 @@
     }
 
     private void tryBindTransport(ServiceInfo transport) {
-        Slog.d(TAG, "Binding to transport: " + transport.getComponentName().flattenToShortString());
+        final ComponentName transportComponentName = transport.getComponentName();
+        Slog.d(TAG, "Binding to transport: " + transportComponentName.flattenToShortString());
         // TODO: b/22388012 (Multi user backup and restore)
-        TransportConnection connection = new TransportConnection(transport.getComponentName());
-        if (bindToTransport(transport.getComponentName(), connection)) {
+        TransportConnection connection = new TransportConnection(transportComponentName);
+        if (bindToTransport(transportComponentName, connection)) {
             synchronized (mTransportLock) {
-                mValidTransports.put(transport.getComponentName(), connection);
+                mValidTransports.put(transportComponentName, connection);
             }
         } else {
-            Slog.w(TAG, "Couldn't bind to transport " + transport.getComponentName());
+            Slog.w(TAG, "Couldn't bind to transport " + transportComponentName);
         }
     }
 
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 02223c1..0d438cb 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -99,6 +99,7 @@
     static final boolean DEBUG_VALIDATE = localLOGV || false;
     static final boolean DEBUG_ALARM_CLOCK = localLOGV || false;
     static final boolean DEBUG_LISTENER_CALLBACK = localLOGV || false;
+    static final boolean DEBUG_WAKELOCK = localLOGV || false;
     static final boolean RECORD_ALARMS_IN_HISTORY = true;
     static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
     static final int ALARM_EVENT = 1;
@@ -856,6 +857,19 @@
             mFilterStats = fs;
             mAlarmType = alarmType;
         }
+
+        @Override
+        public String toString() {
+            return "InFlight{"
+                    + "pendingIntent=" + mPendingIntent
+                    + ", workSource=" + mWorkSource
+                    + ", uid=" + mUid
+                    + ", tag=" + mTag
+                    + ", broadcastStats=" + mBroadcastStats
+                    + ", filterStats=" + mFilterStats
+                    + ", alarmType=" + mAlarmType
+                    + "}";
+        }
     }
 
     static final class FilterStats {
@@ -873,6 +887,19 @@
             mBroadcastStats = broadcastStats;
             mTag = tag;
         }
+
+        @Override
+        public String toString() {
+            return "FilterStats{"
+                    + "tag=" + mTag
+                    + ", lastTime=" + lastTime
+                    + ", aggregateTime=" + aggregateTime
+                    + ", count=" + count
+                    + ", numWakeup=" + numWakeup
+                    + ", startTime=" + startTime
+                    + ", nesting=" + nesting
+                    + "}";
+        }
     }
 
     static final class BroadcastStats {
@@ -890,6 +917,19 @@
             mUid = uid;
             mPackageName = packageName;
         }
+
+        @Override
+        public String toString() {
+            return "BroadcastStats{"
+                    + "uid=" + mUid
+                    + ", packageName=" + mPackageName
+                    + ", aggregateTime=" + aggregateTime
+                    + ", count=" + count
+                    + ", numWakeup=" + numWakeup
+                    + ", startTime=" + startTime
+                    + ", nesting=" + nesting
+                    + "}";
+        }
     }
 
     final SparseArray<ArrayMap<String, BroadcastStats>> mBroadcastStats
@@ -2895,6 +2935,9 @@
                 updateStatsLocked(inflight);
             }
             mBroadcastRefCount--;
+            if (DEBUG_WAKELOCK) {
+                Slog.d(TAG, "mBroadcastRefCount -> " + mBroadcastRefCount);
+            }
             if (mBroadcastRefCount == 0) {
                 mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 0).sendToTarget();
                 mWakeLock.release();
@@ -3035,6 +3078,9 @@
             }
 
             // The alarm is now in flight; now arrange wakelock and stats tracking
+            if (DEBUG_WAKELOCK) {
+                Slog.d(TAG, "mBroadcastRefCount -> " + (mBroadcastRefCount + 1));
+            }
             if (mBroadcastRefCount == 0) {
                 setWakelockWorkSource(alarm.operation, alarm.workSource,
                         alarm.type, alarm.statsTag, (alarm.operation == null) ? alarm.uid : -1,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cace7a9..54ee5dc 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -135,6 +135,8 @@
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
 import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
 import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_RELAUNCH;
 import static com.android.server.wm.AppTransition.TRANSIT_NONE;
@@ -144,6 +146,8 @@
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
+import static java.lang.Integer.MAX_VALUE;
+
 import android.Manifest;
 import android.Manifest.permission;
 import android.annotation.NonNull;
@@ -3072,7 +3076,7 @@
                 final IVoiceInteractionSession session;
 
                 if (mLastResumedActivity.task != null
-                    && mLastResumedActivity.task.voiceSession != null) {
+                        && mLastResumedActivity.task.voiceSession != null) {
                     session = mLastResumedActivity.task.voiceSession;
                 } else {
                     session = mLastResumedActivity.voiceSession;
@@ -4157,13 +4161,9 @@
         synchronized (this) {
             for (int i=mLruProcesses.size()-1; i>=0; i--) {
                 final ProcessRecord proc = mLruProcesses.get(i);
-                if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT
-                        || procState > proc.setProcState) {
-                    if (proc.pkgList.containsKey(packageName)) {
-                        procState = proc.setProcState;
-                        break;
-                    }
-                    if (proc.pkgDeps != null && proc.pkgDeps.contains(packageName)) {
+                if (procState > proc.setProcState) {
+                    if (proc.pkgList.containsKey(packageName) ||
+                            (proc.pkgDeps != null && proc.pkgDeps.contains(packageName))) {
                         procState = proc.setProcState;
                     }
                 }
@@ -9812,14 +9812,19 @@
                 } else if (bounds != null && stackId != FREEFORM_WORKSPACE_STACK_ID ) {
                     stackId = FREEFORM_WORKSPACE_STACK_ID;
                 }
+
+                // Reparent the task to the right stack if necessary
                 boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
                 if (stackId != task.getStackId()) {
-                    mStackSupervisor.moveTaskToStackUncheckedLocked(task, stackId, ON_TOP,
-                            !FORCE_FOCUS, "resizeTask");
+                    // Defer resume until the task is resized below
+                    task.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
+                            DEFER_RESUME, "resizeTask");
                     preserveWindow = false;
                 }
 
-                task.resize(bounds, resizeMode, preserveWindow, false /* deferResume */);
+                // After reparenting (which only resizes the task to the stack bounds), resize the
+                // task to the actual bounds provided
+                task.resize(bounds, resizeMode, preserveWindow, !DEFER_RESUME);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -10198,14 +10203,16 @@
                     throw new IllegalArgumentException(
                             "exitFreeformMode: No activity record matching token=" + token);
                 }
-                final ActivityStack stack = r.getStackLocked(token);
+
+                final ActivityStack stack = r.getStack();
                 if (stack == null || stack.mStackId != FREEFORM_WORKSPACE_STACK_ID) {
                     throw new IllegalStateException(
                             "exitFreeformMode: You can only go fullscreen from freeform.");
                 }
+
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "exitFreeformMode: " + r);
-                mStackSupervisor.moveTaskToStackLocked(r.task.taskId, FULLSCREEN_WORKSPACE_STACK_ID,
-                        ON_TOP, !FORCE_FOCUS, "exitFreeformMode", ANIMATE);
+                r.task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT,
+                        ANIMATE, !DEFER_RESUME, "exitFreeformMode");
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -10222,15 +10229,22 @@
         synchronized (this) {
             long ident = Binder.clearCallingIdentity();
             try {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
+                    return;
+                }
+
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
                         + " to stackId=" + stackId + " toTop=" + toTop);
                 if (stackId == DOCKED_STACK_ID) {
                     mWindowManager.setDockedStackCreateState(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
                             null /* initialBounds */);
                 }
-                boolean result = mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop,
-                        !FORCE_FOCUS, "moveTaskToStack", ANIMATE);
-                if (result && stackId == DOCKED_STACK_ID) {
+
+                final boolean successful = task.reparent(stackId, toTop,
+                        REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "moveTaskToStack");
+                if (successful && stackId == DOCKED_STACK_ID) {
                     // If task moved to docked stack - show recents if needed.
                     mWindowManager.showRecentApps(false /* fromHome */);
                 }
@@ -10262,22 +10276,23 @@
                 // TODO: App transition
                 mWindowManager.prepareAppTransition(TRANSIT_ACTIVITY_RELAUNCH, false);
 
-                // Defer the resume so resume/pausing while moving stacks is dangerous.
-                mStackSupervisor.moveTaskToStackLocked(topTask.taskId, DOCKED_STACK_ID,
-                        false /* toTop */, !FORCE_FOCUS, "swapDockedAndFullscreenStack",
-                        ANIMATE, true /* deferResume */);
+                // Defer the resume until we move all the docked tasks to the fullscreen stack below
+                topTask.reparent(DOCKED_STACK_ID, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
+                        DEFER_RESUME, "swapDockedAndFullscreenStack - DOCKED_STACK");
                 final int size = tasks.size();
                 for (int i = 0; i < size; i++) {
                     final int id = tasks.get(i).taskId;
                     if (id == topTask.taskId) {
                         continue;
                     }
-                    mStackSupervisor.moveTaskToStackLocked(id,
-                            FULLSCREEN_WORKSPACE_STACK_ID, true /* toTop */, !FORCE_FOCUS,
-                            "swapDockedAndFullscreenStack", ANIMATE, true /* deferResume */);
+
+                    // Defer the resume until after all the tasks have been moved
+                    tasks.get(i).reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
+                            REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, DEFER_RESUME,
+                            "swapDockedAndFullscreenStack - FULLSCREEN_STACK");
                 }
 
-                // Because we deferred the resume, to avoid conflicts with stack switches while
+                // Because we deferred the resume to avoid conflicts with stack switches while
                 // resuming, we need to do it after all the tasks are moved.
                 mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                 mStackSupervisor.resumeFocusedStackTopActivityLocked();
@@ -10310,12 +10325,20 @@
         synchronized (this) {
             long ident = Binder.clearCallingIdentity();
             try {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    Slog.w(TAG, "moveTaskToDockedStack: No task for id=" + taskId);
+                    return false;
+                }
+
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
                         + " to createMode=" + createMode + " toTop=" + toTop);
                 mWindowManager.setDockedStackCreateState(createMode, initialBounds);
-                final boolean moved = mStackSupervisor.moveTaskToStackLocked(
-                        taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS, "moveTaskToDockedStack",
-                        animate, DEFER_RESUME);
+
+                // Defer resuming until we move the home stack to the front below
+                final boolean moved = task.reparent(DOCKED_STACK_ID, toTop,
+                        REPARENT_KEEP_STACK_AT_FRONT, animate, DEFER_RESUME,
+                        "moveTaskToDockedStack");
                 if (moved) {
                     if (moveHomeStackFront) {
                         mStackSupervisor.moveHomeStackToFront("moveTaskToDockedStack");
@@ -10449,7 +10472,8 @@
                     stack.positionChildAt(task, position);
                 } else {
                     // Reparent to new stack.
-                    task.reparent(stackId, position, "positionTaskInStack");
+                    task.reparent(stackId, position, REPARENT_LEAVE_STACK_IN_PLACE,
+                            !ANIMATE, !DEFER_RESUME, "positionTaskInStack");
                 }
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -12087,6 +12111,12 @@
         mRecentTasks.notifyTaskPersisterLocked(task, flush);
     }
 
+    /** Notifies all listeners when the pinned stack animation starts. */
+    @Override
+    public void notifyPinnedStackAnimationStarted() {
+        mTaskChangeNotificationController.notifyPinnedStackAnimationStarted();
+    }
+
     /** Notifies all listeners when the pinned stack animation ends. */
     @Override
     public void notifyPinnedStackAnimationEnded() {
@@ -14439,7 +14469,8 @@
         boolean dumpClient = false;
         boolean dumpCheckin = false;
         boolean dumpCheckinFormat = false;
-        boolean dumpVisibleStacks = false;
+        boolean dumpVisibleStacksOnly = false;
+        boolean dumpFocusedStackOnly = false;
         String dumpPackage = null;
 
         int opti = 0;
@@ -14454,7 +14485,9 @@
             } else if ("-c".equals(opt)) {
                 dumpClient = true;
             } else if ("-v".equals(opt)) {
-                dumpVisibleStacks = true;
+                dumpVisibleStacksOnly = true;
+            } else if ("-f".equals(opt)) {
+                dumpFocusedStackOnly = true;
             } else if ("-p".equals(opt)) {
                 if (opti < args.length) {
                     dumpPackage = args[opti];
@@ -14645,7 +14678,8 @@
                 LockGuard.dump(fd, pw, args);
             } else {
                 // Dumping a single activity?
-                if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll, dumpVisibleStacks)) {
+                if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll, dumpVisibleStacksOnly,
+                        dumpFocusedStackOnly)) {
                     ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true);
                     int res = shell.exec(this, null, fd, null, args, null,
                             new ResultReceiver(null));
@@ -15584,13 +15618,17 @@
      *  - the cmd arg isn't the flattened component name of an existing activity:
      *    dump all activity whose component contains the cmd as a substring
      *  - A hex number of the ActivityRecord object instance.
+     *
+     *  @param dumpVisibleStacksOnly dump activity with {@param name} only if in a visible stack
+     *  @param dumpFocusedStackOnly dump activity with {@param name} only if in the focused stack
      */
     protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
-            int opti, boolean dumpAll, boolean dumpVisibleStacks) {
+            int opti, boolean dumpAll, boolean dumpVisibleStacksOnly, boolean dumpFocusedStackOnly) {
         ArrayList<ActivityRecord> activities;
 
         synchronized (this) {
-            activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacks);
+            activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly,
+                    dumpFocusedStackOnly);
         }
 
         if (activities.size() <= 0) {
@@ -18845,7 +18883,9 @@
                     appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
                     resultExtras, ordered, sticky, false, userId);
             if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
-            final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
+            final boolean replaced = replacePending
+                    && (queue.replaceParallelBroadcastLocked(r) != null);
+            // Note: We assume resultTo is null for non-ordered broadcasts.
             if (!replaced) {
                 queue.enqueueParallelBroadcastLocked(r);
                 queue.scheduleBroadcastsLocked();
@@ -18944,8 +18984,25 @@
             if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
                     "Enqueueing broadcast " + r.intent.getAction());
 
-            boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
-            if (!replaced) {
+            final BroadcastRecord oldRecord =
+                    replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
+            if (oldRecord != null) {
+                // Replaced, fire the result-to receiver.
+                if (oldRecord.resultTo != null) {
+                    final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
+                    try {
+                        oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
+                                oldRecord.intent,
+                                Activity.RESULT_CANCELED, null, null,
+                                false, false, oldRecord.userId);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failure ["
+                                + queue.mQueueName + "] sending broadcast result of "
+                                + intent, e);
+
+                    }
+                }
+            } else {
                 queue.enqueueOrderedBroadcastLocked(r);
                 queue.scheduleBroadcastsLocked();
             }
@@ -18962,8 +19019,7 @@
         return ActivityManager.BROADCAST_SUCCESS;
     }
 
-    final void addBroadcastStatLocked(String action, String srcPackage, int receiveCount,
-            int skipCount, long dispatchTime) {
+    final void rotateBroadcastStatsIfNeededLocked() {
         final long now = SystemClock.elapsedRealtime();
         if (mCurBroadcastStats == null ||
                 (mCurBroadcastStats.mStartRealtime +(24*60*60*1000) < now)) {
@@ -18974,9 +19030,19 @@
             }
             mCurBroadcastStats = new BroadcastStats();
         }
+    }
+
+    final void addBroadcastStatLocked(String action, String srcPackage, int receiveCount,
+            int skipCount, long dispatchTime) {
+        rotateBroadcastStatsIfNeededLocked();
         mCurBroadcastStats.addBroadcast(action, srcPackage, receiveCount, skipCount, dispatchTime);
     }
 
+    final void addBackgroundCheckViolationLocked(String action, String targetPackage) {
+        rotateBroadcastStatsIfNeededLocked();
+        mCurBroadcastStats.addBackgroundCheckViolation(action, targetPackage);
+    }
+
     final Intent verifyBroadcastLocked(Intent intent) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors() == true) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 10055c8..c79ed2a 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -797,6 +797,15 @@
                     + " is already the parent of r=" + this);
         }
 
+        // TODO: Ensure that we do not directly reparent activities across stacks, as that may leave
+        //       the stacks in strange states. For now, we should use Task.reparent() to ensure that
+        //       the stack is left in an OK state.
+        if (prevTask != null && newTask != null && prevTask.getStack() != newTask.getStack()) {
+            throw new IllegalArgumentException(reason + ": task=" + newTask
+                    + " is in a different stack (" + newTask.getStackId() + ") than the parent of"
+                    + " r=" + this + " (" + prevTask.getStackId() + ")");
+        }
+
         // Must reparent first in window manager
         mWindowContainerController.reparent(newTask.getWindowContainerController(), position);
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 7f7caff..5d4bff9 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3827,7 +3827,9 @@
         r.app = null;
         r.removeWindowContainer();
         final TaskRecord task = r.task;
-        if (task != null && task.removeActivity(r)) {
+        final boolean lastActivity = task != null ? task.removeActivity(r) : false;
+
+        if (lastActivity) {
             if (DEBUG_STACK) Slog.i(TAG_STACK,
                     "removeActivityFromHistoryLocked: last activity removed from " + this);
             if (mStackSupervisor.isFocusedStack(this) && task == topTask() &&
@@ -4819,7 +4821,7 @@
     }
 
     ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
-        ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
+        ArrayList<ActivityRecord> activities = new ArrayList<>();
 
         if ("all".equals(name)) {
             for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -5075,45 +5077,6 @@
         moveToFront(reason);
     }
 
-    /**
-     * Moves the input activity from its current stack to this one.
-     * NOTE: The current task of the activity isn't moved to this stack. Instead a new task is
-     * created on this stack which the activity is added to.
-     * */
-    void moveActivityToStack(ActivityRecord r) {
-        final ActivityStack prevStack = r.getStack();
-        if (prevStack.mStackId == mStackId) {
-            // You are already in the right stack silly...
-            return;
-        }
-
-        final boolean wasFocused = mStackSupervisor.isFocusedStack(prevStack)
-                && (mStackSupervisor.topRunningActivityLocked() == r);
-        final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r);
-        final boolean wasPaused = prevStack.mPausingActivity == r;
-
-        // Create a new task for the activity to be parented in
-        final TaskRecord task = createTaskRecord(
-                mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
-                r.info, r.intent, null, null, true, r.mActivityType);
-        // This is a new task, so reparenting it to position 0 will move it to the top
-        r.reparent(task, 0 /* position */, "moveActivityToStack");
-
-        // Notify the task actiivties if it was moved to/from a pinned stack
-        mStackSupervisor.scheduleReportPictureInPictureModeChangedIfNeeded(task, prevStack);
-
-        // Resume the activity if necessary after it has moved
-        moveToFrontAndResumeStateIfNeeded(r, wasFocused, wasResumed, wasPaused,
-                "moveActivityToStack");
-        if (wasResumed) {
-            prevStack.mResumedActivity = null;
-        }
-        if (wasPaused) {
-            prevStack.mPausingActivity = null;
-            prevStack.removeTimeoutsForActivityLocked(r);
-        }
-    }
-
     public int getStackId() {
         return mStackId;
     }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index ab1559d..6e138a1 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -89,6 +89,9 @@
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
+import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
 import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
 import static java.lang.Integer.MAX_VALUE;
 
@@ -1992,8 +1995,8 @@
                     stackId = task.getLaunchStackId();
                 }
                 if (stackId != currentStack.mStackId) {
-                    currentStack = moveTaskToStackUncheckedLocked(task, stackId, ON_TOP,
-                            !FORCE_FOCUS, reason);
+                    task.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
+                            DEFER_RESUME, "findTaskToMoveToFrontLocked");
                     stackId = currentStack.mStackId;
                     // moveTaskToStackUncheckedLocked() should already placed the task on top,
                     // still need moveTaskToFrontLocked() below for any transition settings.
@@ -2278,6 +2281,7 @@
             if (onTop) {
                 for (int i = 0; i < size; i++) {
                     final TaskRecord task = tasks.get(i);
+                    final boolean isTopTask = i == (size - 1);
                     if (fromStackId == PINNED_STACK_ID) {
                         // Update the return-to to reflect where the pinned stack task was moved
                         // from so that we retain the stack that was previously visible if the
@@ -2287,22 +2291,25 @@
                         MetricsLogger.action(mService.mContext,
                                 MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
                     }
-                    moveTaskToStackLocked(tasks.get(i).taskId,
-                            FULLSCREEN_WORKSPACE_STACK_ID, onTop, onTop /*forceFocus*/,
-                            "moveTasksToFullscreenStack - onTop", ANIMATE, DEFER_RESUME);
+                    // Defer resume until all the tasks have been moved to the fullscreen stack
+                    task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
+                            REPARENT_MOVE_STACK_TO_FRONT, isTopTask /* animate */, DEFER_RESUME,
+                            "moveTasksToFullscreenStack - onTop");
                 }
-
-                ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-                resumeFocusedStackTopActivityLocked();
             } else {
                 for (int i = 0; i < size; i++) {
                     final TaskRecord task = tasks.get(i);
-                    final int position = fullscreenStack != null ?
-                            Math.max(fullscreenStack.getAllTasks().size() - 1, 0) : 0;
+                    final int position = fullscreenStack != null
+                            ? Math.max(fullscreenStack.getAllTasks().size() - 1, 0) : 0;
+                    // Defer resume until all the tasks have been moved to the fullscreen stack
                     task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, position,
-                            "moveTasksToFullscreenStack - NOT_onTop");
+                            REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE,
+                            DEFER_RESUME, "moveTasksToFullscreenStack - NOT_onTop");
                 }
             }
+
+            ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+            resumeFocusedStackTopActivityLocked();
         } finally {
             mAllowDockedStackResize = true;
             mWindowManager.continueSurfaceLayout();
@@ -2445,14 +2452,16 @@
                             ? Math.max(0, fullscreenStack.getChildCount() - 1)
                             : fullscreenStack.getChildCount();
                     final TaskRecord task = tasks.get(i);
-                    task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, insertPosition, "removeStack");
+                    // Defer resume until we remove all the tasks
+                    task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, insertPosition,
+                            REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME, "removeStack");
                 }
                 ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                 resumeFocusedStackTopActivityLocked();
             } else {
                 // If there is no fullscreen stack, then create the stack and move all the tasks
                 // onto the stack
-                moveTasksToFullscreenStackLocked(PINNED_STACK_ID, false /* onTop */);
+                moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP);
             }
         } else {
             for (int i = tasks.size() - 1; i >= 0; i--) {
@@ -2657,168 +2666,52 @@
     }
 
     /**
-     * Moves the specified task record to the input stack id.
-     * WARNING: This method performs an unchecked/raw move of the task and
-     * can leave the system in an unstable state if used incorrectly.
-     * Use {@link #moveTaskToStackLocked} to perform safe task movement to a stack.
-     * @param task Task to move.
-     * @param stackId Id of stack to move task to.
-     * @param toTop True if the task should be placed at the top of the stack.
-     * @param forceFocus if focus should be moved to the new stack
-     * @param reason Reason the task is been moved.
-     * @return The stack the task was moved to.
+     * Returns the reparent target stack, creating the stack if necessary.  This call also enforces
+     * the various checks on tasks that are going to be reparented from one stack to another.
      */
-    ActivityStack moveTaskToStackUncheckedLocked(TaskRecord task, int stackId, boolean toTop,
-            boolean forceFocus, String reason) {
-
-        if (StackId.isMultiWindowStack(stackId) && !mService.mSupportsMultiWindow) {
-            throw new IllegalStateException("moveTaskToStackUncheckedLocked: Device doesn't "
-                    + "support multi-window task=" + task + " to stackId=" + stackId);
-        }
-
-        final ActivityRecord r = task.topRunningActivityLocked();
+    ActivityStack getReparentTargetStack(TaskRecord task, int stackId, boolean toTop) {
         final ActivityStack prevStack = task.getStack();
-        final boolean wasFocused = isFocusedStack(prevStack) && (topRunningActivityLocked() == r);
-        final boolean wasResumed = prevStack.mResumedActivity == r;
-        final boolean wasPaused = prevStack.mPausingActivity == r;
-        // In some cases the focused stack isn't the front stack. E.g. pinned stack.
-        // Whenever we are moving the top activity from the front stack we want to make sure to move
-        // the stack to the front.
-        final boolean wasFront = isFrontStackOnDisplay(prevStack)
-                && (prevStack.topRunningActivityLocked() == r);
 
-        if (stackId == DOCKED_STACK_ID && !task.isResizeable()) {
-            // We don't allow moving a unresizeable task to the docked stack since the docked
-            // stack is used for split-screen mode and will cause things like the docked divider to
-            // show up. We instead leave the task in its current stack or move it to the fullscreen
-            // stack if it isn't currently in a stack.
-            stackId = (prevStack != null) ? prevStack.mStackId : FULLSCREEN_WORKSPACE_STACK_ID;
-            Slog.w(TAG, "Can not move unresizeable task=" + task
-                    + " to docked stack. Moving to stackId=" + stackId + " instead.");
+        // Check that we aren't reparenting to the same stack that the task is already in
+        if (prevStack != null && prevStack.mStackId == stackId) {
+            Slog.w(TAG, "Can not reparent to same stack, task=" + task
+                    + " already in stackId=" + stackId);
+            return prevStack;
         }
 
-        // Temporarily disable resizeablility of task we are moving. We don't want it to be resized
-        // if a docked stack is created below which will lead to the stack we are moving from and
-        // its resizeable tasks being resized.
-        task.mTemporarilyUnresizable = true;
-        final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop);
-        task.mTemporarilyUnresizable = false;
-        task.reparent(stack.mStackId, toTop ? MAX_VALUE : 0, reason);
-
-        // Reset the resumed activity on the previous stack
-        if (wasResumed) {
-            prevStack.mResumedActivity = null;
-        }
-        // Reset the paused activity on the previous stack
-        if (wasPaused) {
-            prevStack.mPausingActivity = null;
-            prevStack.removeTimeoutsForActivityLocked(r);
+        // Ensure that we aren't trying to move into a multi-window stack without multi-window
+        // support
+        if (StackId.isMultiWindowStack(stackId) && !mService.mSupportsMultiWindow) {
+            throw new IllegalArgumentException("Device doesn't support multi-window, can not"
+                    + " reparent task=" + task + " to stackId=" + stackId);
         }
 
-        // 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.
-        stack.moveToFrontAndResumeStateIfNeeded(r, forceFocus || wasFocused || wasFront, wasResumed,
-                wasPaused, reason);
-
-        return stack;
-    }
-
-    boolean moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
-            String reason, boolean animate) {
-        return moveTaskToStackLocked(taskId, stackId, toTop, forceFocus, reason, animate,
-                false /* deferResume */);
-    }
-
-    boolean moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
-            String reason, boolean animate, boolean deferResume) {
-        final TaskRecord task = anyTaskForIdLocked(taskId);
-        if (task == null) {
-            Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId);
-            return false;
-        }
-
-        final ActivityStack currentStack = task.getStack();
-        if (currentStack != null && currentStack.mStackId == stackId) {
-            // You are already in the right stack silly...
-            Slog.i(TAG, "moveTaskToStack: taskId=" + taskId + " already in stackId=" + stackId);
-            return true;
-        }
-
+        // Ensure that we aren't trying to move into a freeform stack without freeform
+        // support
         if (stackId == FREEFORM_WORKSPACE_STACK_ID && !mService.mSupportsFreeformWindowManagement) {
-            throw new IllegalArgumentException("moveTaskToStack:"
-                    + "Attempt to move task " + taskId + " to unsupported freeform stack");
+            throw new IllegalArgumentException("Device doesn't support freeform, can not reparent"
+                    + " task=" + task);
         }
 
-        final ActivityRecord topActivity = task.getTopActivity();
-        final int sourceStackId = task.getStackId();
-        final boolean mightReplaceWindow =
-                StackId.replaceWindowsOnTaskMove(sourceStackId, stackId) && topActivity != null;
-        if (mightReplaceWindow) {
-            // We are about to relaunch the activity because its configuration changed due to
-            // being maximized, i.e. size change. The activity will first remove the old window
-            // and then add a new one. This call will tell window manager about this, so it can
-            // preserve the old window until the new one is drawn. This prevents having a gap
-            // between the removal and addition, in which no window is visible. We also want the
-            // entrance of the new window to be properly animated.
-            // Note here we always set the replacing window first, as the flags might be needed
-            // during the relaunch. If we end up not doing any relaunch, we clear the flags later.
-            mWindowManager.setWillReplaceWindow(topActivity.appToken, animate);
+        // We don't allow moving a unresizeable task to the docked stack since the docked stack is
+        // used for split-screen mode and will cause things like the docked divider to show up. We
+        // instead leave the task in its current stack or move it to the fullscreen stack if it
+        // isn't currently in a stack.
+        if (stackId == DOCKED_STACK_ID && !task.isResizeable()) {
+            stackId = (prevStack != null) ? prevStack.mStackId : FULLSCREEN_WORKSPACE_STACK_ID;
+            Slog.w(TAG, "Can not move unresizeable task=" + task + " to docked stack."
+                    + " Moving to stackId=" + stackId + " instead.");
         }
 
-        mWindowManager.deferSurfaceLayout();
-        final int preferredLaunchStackId = stackId;
-        boolean kept = true;
+        // Temporarily disable resizeablility of the task as we don't want it to be resized if, for
+        // example, a docked stack is created which will lead to the stack we are moving from being
+        // resized and and its resizeable tasks being resized.
         try {
-            final ActivityStack stack = moveTaskToStackUncheckedLocked(
-                    task, stackId, toTop, forceFocus, reason + " moveTaskToStack");
-            stackId = stack.mStackId;
-
-            if (!animate) {
-                stack.mNoAnimActivities.add(topActivity);
-            }
-
-            // We might trigger a configuration change. Save the current task bounds for freezing.
-            // TODO: Should this call be moved inside the resize method in WM?
-            stack.prepareFreezingTaskBounds();
-
-            // Make sure the task has the appropriate bounds/size for the stack it is in.
-            if (stackId == FULLSCREEN_WORKSPACE_STACK_ID
-                    && !Objects.equals(task.mBounds, stack.mBounds)) {
-                kept = task.resize(stack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
-                        deferResume);
-            } else if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
-                Rect bounds = task.getLaunchBounds();
-                if (bounds == null) {
-                    stack.layoutTaskInStack(task, null);
-                    bounds = task.mBounds;
-                }
-                kept = task.resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
-            } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
-                kept = task.resize(stack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
-                        deferResume);
-            }
+            task.mTemporarilyUnresizable = true;
+            return getStack(stackId, CREATE_IF_NEEDED, toTop);
         } finally {
-            mWindowManager.continueSurfaceLayout();
+            task.mTemporarilyUnresizable = false;
         }
-
-        if (mightReplaceWindow) {
-            // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
-            // window), we need to clear the replace window settings. Otherwise, we schedule a
-            // timeout to remove the old window if the replacing window is not coming in time.
-            mWindowManager.scheduleClearWillReplaceWindows(topActivity.appToken, !kept);
-        }
-
-        if (!deferResume) {
-
-            // The task might have already been running and its visibility needs to be synchronized with
-            // the visibility of the stack / windows.
-            ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
-            resumeFocusedStackTopActivityLocked();
-        }
-
-        handleNonResizableTaskIfNeeded(task, preferredLaunchStackId, stackId);
-
-        return (preferredLaunchStackId == stackId);
     }
 
     boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect bounds) {
@@ -2849,8 +2742,8 @@
 
     void moveActivityToPinnedStackLocked(ActivityRecord r, String reason, Rect bounds,
             boolean moveHomeStackToFront) {
-
         mWindowManager.deferSurfaceLayout();
+
         // Need to make sure the pinned stack exist so we can resize it below...
         final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
 
@@ -2882,12 +2775,26 @@
                     // was launched from home so home should be visible behind it.
                     moveHomeStackToFront(reason);
                 }
-                moveTaskToStackLocked(
-                        task.taskId, PINNED_STACK_ID, ON_TOP, FORCE_FOCUS, reason, !ANIMATE);
+                // Defer resume until below
+                task.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+                        DEFER_RESUME, reason);
             } else {
                 // There are multiple activities in the task and moving the top activity should
-                // reveal/leave the other activities in their original task
-                stack.moveActivityToStack(r);
+                // reveal/leave the other activities in their original task.
+
+                // Currently, we don't support reparenting activities across tasks in two different
+                // stacks, so instead, just create a new task in the same stack, reparent the
+                // activity into that task, and then reparent the whole task to the new stack. This
+                // ensures that all the necessary work to migrate states in the old and new stacks
+                // is also done.
+                final TaskRecord newTask = task.getStack().createTaskRecord(
+                        getNextTaskIdForUserLocked(r.userId), r.info, r.intent, null, null, true,
+                        r.mActivityType);
+                r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
+
+                // Defer resume until below
+                newTask.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+                        DEFER_RESUME, reason);
             }
 
             // Reset the state that indicates it can enter PiP while pausing after we've moved it
@@ -3521,22 +3428,24 @@
      * Dumps the activities matching the given {@param name} in the either the focused stack
      * or all visible stacks if {@param dumpVisibleStacks} is true.
      */
-    ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, boolean dumpVisibleStacks) {
-        if (dumpVisibleStacks) {
+    ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, boolean dumpVisibleStacksOnly,
+            boolean dumpFocusedStackOnly) {
+        if (dumpFocusedStackOnly) {
+            return mFocusedStack.getDumpActivitiesLocked(name);
+        } else {
             ArrayList<ActivityRecord> activities = new ArrayList<>();
             int numDisplays = mActivityDisplays.size();
             for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
                 ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
                 for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                     ActivityStack stack = stacks.get(stackNdx);
-                    if (stack.getStackVisibilityLocked(null) == STACK_VISIBLE) {
+                    if (!dumpVisibleStacksOnly ||
+                            stack.getStackVisibilityLocked(null) == STACK_VISIBLE) {
                         activities.addAll(stack.getDumpActivitiesLocked(name));
                     }
                 }
             }
             return activities;
-        } else {
-            return mFocusedStack.getDumpActivitiesLocked(name);
         }
     }
 
@@ -4878,9 +4787,8 @@
 
         if (launchStackId != INVALID_STACK_ID) {
             if (task.getStackId() != launchStackId) {
-                moveTaskToStackLocked(
-                        taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents",
-                        ANIMATE);
+                task.reparent(launchStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE,
+                        DEFER_RESUME, "startActivityFromRecents");
             }
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index d8c5533..b9ae4fd 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -77,11 +77,16 @@
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
 import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
+import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
 import static com.android.server.am.EventLogTags.AM_NEW_INTENT;
+import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
+
+import static java.lang.Integer.MAX_VALUE;
 
 import android.annotation.NonNull;
 import android.app.ActivityManager;
@@ -100,7 +105,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.AuxiliaryResolveInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
@@ -509,22 +513,23 @@
         doPendingActivityLaunchesLocked(false);
 
         return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true,
-            options, inTask);
+                options, inTask);
     }
 
-    /** Creates a launch intent for the given auxiliary resolution data. */
+    /**
+     * Creates a launch intent for the given auxiliary resolution data.
+     */
     private @NonNull Intent createLaunchIntent(@NonNull AuxiliaryResolveInfo auxiliaryResponse,
-        Intent originalIntent, String callingPackage,
-        String resolvedType, int userId) {
+            Intent originalIntent, String callingPackage, String resolvedType, int userId) {
         if (auxiliaryResponse.needsPhaseTwo) {
             // request phase two resolution
             mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo(
-                auxiliaryResponse, originalIntent, resolvedType, callingPackage, userId);
+                    auxiliaryResponse, originalIntent, resolvedType, callingPackage, userId);
         }
         return EphemeralResolver.buildEphemeralInstallerIntent(originalIntent,
-            callingPackage, resolvedType, userId, auxiliaryResponse.packageName,
-            auxiliaryResponse.splitName, auxiliaryResponse.versionCode,
-            auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo);
+                callingPackage, resolvedType, userId, auxiliaryResponse.packageName,
+                auxiliaryResponse.splitName, auxiliaryResponse.versionCode,
+                auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo);
     }
 
     void postStartActivityProcessing(
@@ -930,14 +935,13 @@
     }
 
     private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
-        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-        int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
         int result = START_CANCELED;
         try {
             mService.mWindowManager.deferSurfaceLayout();
             result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
-                startFlags,
-                doResume, options, inTask);
+                    startFlags, doResume, options, inTask);
         } finally {
             // If we are not able to proceed, disassociate the activity from the task. Leaving an
             // activity in an incomplete state can lead to issues, such as performing operations
@@ -948,8 +952,8 @@
             mService.mWindowManager.continueSurfaceLayout();
         }
 
-        postStartActivityProcessing(r, result, mSupervisor.mFocusedStack.mStackId,
-            mSourceRecord, mTargetStack);
+        postStartActivityProcessing(r, result, mSupervisor.mFocusedStack.mStackId,  mSourceRecord,
+                mTargetStack);
 
         return result;
     }
@@ -1000,11 +1004,22 @@
             if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                     || isDocumentLaunchesIntoExisting(mLaunchFlags)
                     || mLaunchSingleInstance || mLaunchSingleTask) {
+                final TaskRecord task = mReusedActivity.task;
+
                 // In this situation we want to remove all activities from the task up to the one
                 // being started. In most cases this means we are resetting the task to its initial
                 // state.
-                final ActivityRecord top = mReusedActivity.task.performClearTaskForReuseLocked(
-                        mStartActivity, mLaunchFlags);
+                final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
+                        mLaunchFlags);
+
+                // The above code can remove {@code mReusedActivity} from the task, leading to the
+                // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The
+                // task reference is needed in the call below to
+                // {@link setTargetStackAndMoveToFrontIfNeeded}.
+                if (mReusedActivity.task == null) {
+                    mReusedActivity.task = task;
+                }
+
                 if (top != null) {
                     if (top.frontOfTask) {
                         // Activity aliases may mean we use different intents for the top activity,
@@ -1479,9 +1494,9 @@
                         if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
                             // If we want to launch adjacent and mTargetStack is not the computed
                             // launch stack - move task to top of computed stack.
-                            mSupervisor.moveTaskToStackLocked(intentActivity.task.taskId,
-                                    launchStack.mStackId, ON_TOP, FORCE_FOCUS, "launchToSide",
-                                    ANIMATE);
+                            intentActivity.task.reparent(launchStack.mStackId, ON_TOP,
+                                    REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
+                                    "launchToSide");
                         } else {
                             // TODO: This should be reevaluated in MW v2.
                             // We choose to move task to front instead of launching it adjacent
@@ -1546,9 +1561,13 @@
                 == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
             // The caller has requested to completely replace any existing task with its new
             // activity. Well that should not be too hard...
-            intentActivity.task.performClearTaskLocked();
-            intentActivity.task.setIntent(mStartActivity);
+            // Note: we must persist the {@link TaskRecord} first as intentActivity could be
+            // removed from calling performClearTaskLocked (For example, if it is being brought out
+            // of history or if it is finished immediately), thus disassociating the task.
             mReuseTask = intentActivity.task;
+            mReuseTask.performClearTaskLocked();
+            mReuseTask.setIntent(mStartActivity);
+
             // When we clear the task - focus will be adjusted, which will bring another task
             // to top before we launch the activity we need. This will temporary swap their
             // mTaskToReturnTo values and we don't want to overwrite them accidentally.
@@ -1562,6 +1581,9 @@
                 // running, and the caller has asked to clear the current task to have this
                 // activity at the top.
                 mAddingToTask = true;
+
+                // We are no longer placing the activity in the task we previously thought we were.
+                mStartActivity.task = null;
                 // Now pretend like this activity is being started by the top of its task, so it
                 // is put in the right place.
                 mSourceRecord = intentActivity;
@@ -1693,8 +1715,8 @@
         if (mTargetStack == null) {
             mTargetStack = sourceStack;
         } else if (mTargetStack != sourceStack) {
-            mSupervisor.moveTaskToStackLocked(sourceTask.taskId, mTargetStack.mStackId,
-                    ON_TOP, FORCE_FOCUS, "launchToSide", !ANIMATE);
+            sourceTask.reparent(mTargetStack.mStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
+                    !ANIMATE, DEFER_RESUME, "launchToSide");
         }
 
         final TaskRecord topTask = mTargetStack.topTask();
@@ -1760,9 +1782,9 @@
             mInTask.updateOverrideConfiguration(mLaunchBounds);
             int stackId = mInTask.getLaunchStackId();
             if (stackId != mInTask.getStackId()) {
-                final ActivityStack stack = mSupervisor.moveTaskToStackUncheckedLocked(mInTask,
-                        stackId, ON_TOP, !FORCE_FOCUS, "inTaskToFront");
-                stackId = stack.mStackId;
+                mInTask.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
+                        DEFER_RESUME, "inTaskToFront");
+                stackId = mInTask.getStackId();
             }
             if (StackId.resizeStackWithLaunchBounds(stackId)) {
                 mService.resizeStack(stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
@@ -1862,7 +1884,7 @@
             final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
             try {
                 startActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags, resume, null,
-                    null);
+                        null);
             } catch (Exception e) {
                 Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
                 pal.sendErrorResult(e.getMessage());
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 68253c78..c9d19cb 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -239,33 +239,37 @@
         }
     }
 
-    public final boolean replaceParallelBroadcastLocked(BroadcastRecord r) {
-        final Intent intent = r.intent;
-        for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
-            final Intent curIntent = mParallelBroadcasts.get(i).intent;
-            if (intent.filterEquals(curIntent)) {
-                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
-                        "***** DROPPING PARALLEL ["
-                + mQueueName + "]: " + intent);
-                mParallelBroadcasts.set(i, r);
-                return true;
-            }
-        }
-        return false;
+    /**
+     * Find the same intent from queued parallel broadcast, replace with a new one and return
+     * the old one.
+     */
+    public final BroadcastRecord replaceParallelBroadcastLocked(BroadcastRecord r) {
+        return replaceBroadcastLocked(mParallelBroadcasts, r, "PARALLEL");
     }
 
-    public final boolean replaceOrderedBroadcastLocked(BroadcastRecord r) {
+    /**
+     * Find the same intent from queued ordered broadcast, replace with a new one and return
+     * the old one.
+     */
+    public final BroadcastRecord replaceOrderedBroadcastLocked(BroadcastRecord r) {
+        return replaceBroadcastLocked(mOrderedBroadcasts, r, "ORDERED");
+    }
+
+    private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> queue,
+            BroadcastRecord r, String typeForLogging) {
         final Intent intent = r.intent;
-        for (int i = mOrderedBroadcasts.size() - 1; i > 0; i--) {
-            if (intent.filterEquals(mOrderedBroadcasts.get(i).intent)) {
-                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
-                        "***** DROPPING ORDERED ["
-                        + mQueueName + "]: " + intent);
-                mOrderedBroadcasts.set(i, r);
-                return true;
+        for (int i = queue.size() - 1; i > 0; i--) {
+            final BroadcastRecord old = queue.get(i);
+            if (old.userId == r.userId && intent.filterEquals(old.intent)) {
+                if (DEBUG_BROADCAST) {
+                    Slog.v(TAG_BROADCAST, "***** DROPPING "
+                            + typeForLogging + " [" + mQueueName + "]: " + intent);
+                }
+                queue.set(i, r);
+                return old;
             }
         }
-        return false;
+        return null;
     }
 
     private final void processCurBroadcastLocked(BroadcastRecord r,
@@ -1191,6 +1195,8 @@
                                 && r.intent.getPackage() == null
                                 && ((r.intent.getFlags()
                                         & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0))) {
+                        mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
+                                component.getPackageName());
                         Slog.w(TAG, "Background execution not allowed: receiving "
                                 + r.intent + " to "
                                 + component.flattenToShortString());
diff --git a/services/core/java/com/android/server/am/BroadcastStats.java b/services/core/java/com/android/server/am/BroadcastStats.java
index fdbaada..fd24582 100644
--- a/services/core/java/com/android/server/am/BroadcastStats.java
+++ b/services/core/java/com/android/server/am/BroadcastStats.java
@@ -47,6 +47,7 @@
     static final class ActionEntry {
         final String mAction;
         final ArrayMap<String, PackageEntry> mPackages = new ArrayMap<>();
+        final ArrayMap<String, ViolationEntry> mBackgroundCheckViolations = new ArrayMap<>();
         int mReceiveCount;
         int mSkipCount;
         long mTotalDispatchTime;
@@ -61,6 +62,10 @@
         int mSendCount;
     }
 
+    static final class ViolationEntry {
+        int mCount;
+    }
+
     public BroadcastStats() {
         mStartRealtime = SystemClock.elapsedRealtime();
         mStartUptime = SystemClock.uptimeMillis();
@@ -87,6 +92,20 @@
         pe.mSendCount++;
     }
 
+    public void addBackgroundCheckViolation(String action, String targetPackage) {
+        ActionEntry ae = mActions.get(action);
+        if (ae == null) {
+            ae = new ActionEntry(action);
+            mActions.put(action, ae);
+        }
+        ViolationEntry ve = ae.mBackgroundCheckViolations.get(targetPackage);
+        if (ve == null) {
+            ve = new ViolationEntry();
+            ae.mBackgroundCheckViolations.put(targetPackage, ve);
+        }
+        ve.mCount++;
+    }
+
     public boolean dumpStats(PrintWriter pw, String prefix, String dumpPackage) {
         boolean printedSomething = false;
         ArrayList<ActionEntry> actions = new ArrayList<>(mActions.size());
@@ -123,6 +142,15 @@
                 pw.print(pe.mSendCount);
                 pw.println(" times");
             }
+            for (int j=ae.mBackgroundCheckViolations.size()-1; j>=0; j--) {
+                pw.print(prefix);
+                pw.print("  Bg Check Violation ");
+                pw.print(ae.mBackgroundCheckViolations.keyAt(j));
+                pw.print(": ");
+                ViolationEntry ve = ae.mBackgroundCheckViolations.valueAt(j);
+                pw.print(ve.mCount);
+                pw.println(" times");
+            }
         }
         return printedSomething;
     }
@@ -158,6 +186,14 @@
                 pw.print(pe.mSendCount);
                 pw.println();
             }
+            for (int j=ae.mBackgroundCheckViolations.size()-1; j>=0; j--) {
+                pw.print("v,");
+                pw.print(ae.mBackgroundCheckViolations.keyAt(j));
+                ViolationEntry ve = ae.mBackgroundCheckViolations.valueAt(j);
+                pw.print(",");
+                pw.print(ve.mCount);
+                pw.println();
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
index 9dfc7cd..9a98bc6 100644
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -46,6 +46,7 @@
     static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13;
     static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14;
     static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15;
+    static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16;
 
     // Delay in notifying task stack change listeners (in millis)
     static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -100,6 +101,10 @@
         l.onPinnedActivityRestartAttempt((String) m.obj);
     };
 
+    private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> {
+        l.onPinnedStackAnimationStarted();
+    };
+
     private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> {
         l.onPinnedStackAnimationEnded();
     };
@@ -166,6 +171,9 @@
                 case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG:
                     forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg);
                     break;
+                case NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG:
+                    forAllRemoteListeners(mNotifyPinnedStackAnimationStarted, msg);
+                    break;
                 case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG:
                     forAllRemoteListeners(mNotifyPinnedStackAnimationEnded, msg);
                     break;
@@ -276,6 +284,15 @@
         msg.sendToTarget();
     }
 
+    /** Notifies all listeners when the pinned stack animation starts. */
+    void notifyPinnedStackAnimationStarted() {
+        mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
+        final Message msg =
+                mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
+        forAllLocalListeners(mNotifyPinnedStackAnimationStarted, msg);
+        msg.sendToTarget();
+    }
+
     /** Notifies all listeners when the pinned stack animation ends. */
     void notifyPinnedStackAnimationEnded() {
         mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index b051304..3f33f41 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -54,6 +55,7 @@
 import com.android.server.wm.StackWindowController;
 import com.android.server.wm.TaskWindowContainerController;
 import com.android.server.wm.TaskWindowContainerListener;
+import com.android.server.wm.WindowManagerService;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -62,10 +64,13 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Objects;
 
 import static android.app.ActivityManager.RESIZE_MODE_FORCED;
+import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
 import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
@@ -107,9 +112,10 @@
 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.ActivityStackSupervisor.CREATE_IF_NEEDED;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 
+import static java.lang.Integer.MAX_VALUE;
+
 final class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
@@ -160,6 +166,24 @@
     static final int INVALID_TASK_ID = -1;
     private static final int INVALID_MIN_SIZE = -1;
 
+    /**
+     * The modes to control how the stack is moved to the front when calling
+     * {@link TaskRecord#reparent}.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            REPARENT_MOVE_STACK_TO_FRONT,
+            REPARENT_KEEP_STACK_AT_FRONT,
+            REPARENT_LEAVE_STACK_IN_PLACE
+    })
+    public @interface ReparentMoveStackMode {}
+    // Moves the stack to the front if it was not at the front
+    public static final int REPARENT_MOVE_STACK_TO_FRONT = 0;
+    // Only moves the stack to the front if it was focused or front most already
+    public static final int REPARENT_KEEP_STACK_AT_FRONT = 1;
+    // Do not move the stack as a part of reparenting
+    public static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
+
     final int taskId;       // Unique identifier for this task.
     String affinity;        // The affinity name for this task, or null; may change identity.
     String rootAffinity;    // Initial base affinity, or null; does not change from initial root.
@@ -537,36 +561,159 @@
         mWindowContainerController.getBounds(bounds);
     }
 
-    // TODO: Should we be doing all the stuff in ASS.moveTaskToStackLocked?
-    void reparent(int stackId, int position, String reason) {
-        mService.mWindowManager.deferSurfaceLayout();
+    /**
+     * Convenience method to reparent a task to the top or bottom position of the stack.
+     */
+    boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode,
+            boolean animate, boolean deferResume, String reason) {
+        return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate,
+                deferResume, reason);
+    }
 
+    /**
+     * Reparents the task into a preferred stack, creating it if necessary.
+     *
+     * @param preferredStackId the stack id of the target stack to move this task
+     * @param position the position to place this task in the new stack
+     * @param animate whether or not we should wait for the new window created as a part of the
+     *                reparenting to be drawn and animated in
+     * @param moveStackMode whether or not to move the stack to the front always, only if it was
+     *                      previously focused & in front, or never
+     * @param deferResume whether or not to update the visibility of other tasks and stacks that may
+     *                    have changed as a result of this reparenting
+     * @param reason the caller of this reparenting
+     * @return
+     */
+    boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode,
+            boolean animate, boolean deferResume, String reason) {
+        final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
+        final WindowManagerService windowManager = mService.mWindowManager;
+        final ActivityStack sourceStack = getStack();
+        final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStackId,
+                position == MAX_VALUE);
+        if (toStack == sourceStack) {
+            return false;
+        }
+
+        final int sourceStackId = getStackId();
+        final int stackId = toStack.getStackId();
+        final ActivityRecord topActivity = getTopActivity();
+
+        final boolean mightReplaceWindow = StackId.replaceWindowsOnTaskMove(sourceStackId, stackId)
+                && topActivity != null;
+        if (mightReplaceWindow) {
+            // We are about to relaunch the activity because its configuration changed due to
+            // being maximized, i.e. size change. The activity will first remove the old window
+            // and then add a new one. This call will tell window manager about this, so it can
+            // preserve the old window until the new one is drawn. This prevents having a gap
+            // between the removal and addition, in which no window is visible. We also want the
+            // entrance of the new window to be properly animated.
+            // Note here we always set the replacing window first, as the flags might be needed
+            // during the relaunch. If we end up not doing any relaunch, we clear the flags later.
+            windowManager.setWillReplaceWindow(topActivity.appToken, animate);
+        }
+
+        windowManager.deferSurfaceLayout();
+        boolean kept = true;
         try {
-            final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
-            final ActivityStack newStack = supervisor.getStack(stackId,
-                    CREATE_IF_NEEDED, false /* toTop */);
+            final ActivityRecord r = topRunningActivityLocked();
+            final boolean wasFocused = supervisor.isFocusedStack(sourceStack)
+                    && (topRunningActivityLocked() == r);
+            final boolean wasResumed = sourceStack.mResumedActivity == r;
+            final boolean wasPaused = sourceStack.mPausingActivity == r;
+
+            // In some cases the focused stack isn't the front stack. E.g. pinned stack.
+            // Whenever we are moving the top activity from the front stack we want to make sure to
+            // move the stack to the front.
+            final boolean wasFront = supervisor.isFrontStackOnDisplay(sourceStack)
+                    && (sourceStack.topRunningActivityLocked() == r);
+
             // Adjust the position for the new parent stack as needed.
-            position = newStack.getAdjustedPositionForTask(this, position, null /* starting */);
+            position = toStack.getAdjustedPositionForTask(this, position, null /* starting */);
 
             // Must reparent first in window manager to avoid a situation where AM can delete the
             // we are coming from in WM before we reparent because it became empty.
-            mWindowContainerController.reparent(newStack.getWindowContainerController(), position);
+            mWindowContainerController.reparent(toStack.getWindowContainerController(), position);
 
-            final ActivityStack prevStack = mStack;
-            prevStack.removeTask(this, reason, REMOVE_TASK_MODE_MOVING);
-            newStack.addTask(this, position, reason);
+            // Reset the resumed activity on the previous stack
+            if (wasResumed) {
+                sourceStack.mResumedActivity = null;
+            }
 
-            supervisor.scheduleReportPictureInPictureModeChangedIfNeeded(this, prevStack);
+            // Reset the paused activity on the previous stack
+            if (wasPaused) {
+                sourceStack.mPausingActivity = null;
+                sourceStack.removeTimeoutsForActivityLocked(r);
+            }
 
+            // Move the task
+            sourceStack.removeTask(this, reason, REMOVE_TASK_MODE_MOVING);
+            toStack.addTask(this, position, reason);
+
+            // TODO: Ensure that this is actually necessary here
+            // Notify of picture-in-picture mode changes
+            supervisor.scheduleReportPictureInPictureModeChangedIfNeeded(this, sourceStack);
+
+            // TODO: Ensure that this is actually necessary here
+            // Notify the voice session if required
             if (voiceSession != null) {
                 try {
                     voiceSession.taskStarted(intent, taskId);
                 } catch (RemoteException e) {
                 }
             }
+
+            // 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));
+            toStack.moveToFrontAndResumeStateIfNeeded(r, moveStackToFront, wasResumed, wasPaused,
+                    reason);
+            if (!animate) {
+                toStack.mNoAnimActivities.add(topActivity);
+            }
+
+            // We might trigger a configuration change. Save the current task bounds for freezing.
+            // TODO: Should this call be moved inside the resize method in WM?
+            toStack.prepareFreezingTaskBounds();
+
+            // Make sure the task has the appropriate bounds/size for the stack it is in.
+            if (stackId == FULLSCREEN_WORKSPACE_STACK_ID
+                    && !Objects.equals(mBounds, toStack.mBounds)) {
+                kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
+                        deferResume);
+            } else if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
+                Rect bounds = getLaunchBounds();
+                if (bounds == null) {
+                    toStack.layoutTaskInStack(this, null);
+                    bounds = mBounds;
+                }
+                kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume);
+            } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
+                kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
+                        deferResume);
+            }
         } finally {
-            mService.mWindowManager.continueSurfaceLayout();
+            windowManager.continueSurfaceLayout();
         }
+
+        if (mightReplaceWindow) {
+            // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
+            // window), we need to clear the replace window settings. Otherwise, we schedule a
+            // timeout to remove the old window if the replacing window is not coming in time.
+            windowManager.scheduleClearWillReplaceWindows(topActivity.appToken, !kept);
+        }
+
+        if (!deferResume) {
+            // The task might have already been running and its visibility needs to be synchronized
+            // with the visibility of the stack / windows.
+            supervisor.ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
+            supervisor.resumeFocusedStackTopActivityLocked();
+        }
+
+        supervisor.handleNonResizableTaskIfNeeded(this, preferredStackId, stackId);
+
+        return (preferredStackId == stackId);
     }
 
     void cancelWindowTransition() {
@@ -1081,11 +1228,13 @@
         r.onOverrideConfigurationSent();
     }
 
-    /** @return true if this was the last activity in the task */
+    /**
+     * @return true if this was the last activity in the task
+     */
     boolean removeActivity(ActivityRecord r) {
         if (r.task != this) {
             throw new IllegalArgumentException(
-                "Activity=" + r + " does not belong to task=" + this);
+                    "Activity=" + r + " does not belong to task=" + this);
         }
 
         r.task = null;
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index 42c31b1..6710bdc 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -16,7 +16,6 @@
 
 package com.android.server.am;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
@@ -80,10 +79,11 @@
     }
 
     public void setState(int newState) {
-        if (DEBUG_MU) {
-            Slog.i(TAG, "User " + mHandle.getIdentifier() + " state changed from "
-                    + stateToString(state) + " to " + stateToString(newState));
+        if (newState == state) {
+            return;
         }
+        Slog.i(TAG, "User " + mHandle.getIdentifier() + " state changed from "
+                + stateToString(state) + " to " + stateToString(newState));
         lastState = state;
         state = newState;
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index dae5da3..0e767da 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3050,6 +3050,10 @@
             throw new IllegalArgumentException("null not allowed: pkg=" + pkg
                     + " id=" + id + " notification=" + notification);
         }
+
+        // The system can post notifications for any package, let us resolve that.
+        final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
+
         // Fix the notification as best we can.
         try {
             final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
@@ -3069,13 +3073,13 @@
             channelId = (new Notification.TvExtender(notification)).getChannel();
         }
         final NotificationChannel channel =  mRankingHelper.getNotificationChannelWithFallback(pkg,
-                callingUid, channelId, false /* includeDeleted */);
+                notificationUid, channelId, false /* includeDeleted */);
         final StatusBarNotification n = new StatusBarNotification(
-                pkg, opPkg, id, tag, callingUid, callingPid, notification,
+                pkg, opPkg, id, tag, notificationUid, callingPid, notification,
                 user, null, System.currentTimeMillis());
         final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
 
-        if (!checkDisqualifyingFeatures(userId, callingUid, id,tag, r)) {
+        if (!checkDisqualifyingFeatures(userId, notificationUid, id,tag, r)) {
             return;
         }
 
@@ -3101,6 +3105,19 @@
         idOut[0] = id;
     }
 
+    private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
+        // The system can post notifications on behalf of any package it wants
+        if (isCallerSystem() && opPackageName != null && !"android".equals(opPackageName)) {
+            try {
+                return getContext().getPackageManager()
+                        .getPackageUidAsUser(opPackageName, userId);
+            } catch (NameNotFoundException e) {
+                /* ignore */
+            }
+        }
+        return callingUid;
+    }
+
     /**
      * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
      *
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 867af9a..476eb10 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -66,6 +66,7 @@
 
     private static final String ATT_VERSION = "version";
     private static final String ATT_NAME = "name";
+    private static final String ATT_NAME_RES_ID = "name_res_id";
     private static final String ATT_UID = "uid";
     private static final String ATT_ID = "id";
     private static final String ATT_PRIORITY = "priority";
@@ -191,9 +192,14 @@
                             if (TAG_GROUP.equals(tagName)) {
                                 String id = parser.getAttributeValue(null, ATT_ID);
                                 CharSequence groupName = parser.getAttributeValue(null, ATT_NAME);
+                                int groupNameRes = safeInt(parser, ATT_NAME_RES_ID, 0);
                                 if (!TextUtils.isEmpty(id)) {
-                                    final NotificationChannelGroup group =
-                                            new NotificationChannelGroup(id, groupName);
+                                    NotificationChannelGroup group = null;
+                                    if (groupName != null) {
+                                        group = new NotificationChannelGroup(id, groupName);
+                                    } else {
+                                        group = new NotificationChannelGroup(id, groupNameRes);
+                                    }
                                     r.groups.put(id, group);
                                 }
                             }
@@ -201,12 +207,19 @@
                             if (TAG_CHANNEL.equals(tagName)) {
                                 String id = parser.getAttributeValue(null, ATT_ID);
                                 CharSequence channelName = parser.getAttributeValue(null, ATT_NAME);
+                                int channelNameRes = safeInt(parser, ATT_NAME_RES_ID, 0);
                                 int channelImportance =
                                         safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
 
                                 if (!TextUtils.isEmpty(id)) {
-                                    final NotificationChannel channel = new NotificationChannel(id,
-                                            channelName, channelImportance);
+                                    NotificationChannel channel;
+                                    if (channelName != null) {
+                                        channel = new NotificationChannel(id, channelName,
+                                                channelImportance);
+                                    } else {
+                                        channel = new NotificationChannel(id, channelNameRes,
+                                                channelImportance);
+                                    }
                                     channel.populateFromXml(parser);
                                     r.channels.put(id, channel);
                                 }
@@ -286,7 +299,7 @@
             NotificationChannel channel;
             channel = new NotificationChannel(
                     NotificationChannel.DEFAULT_CHANNEL_ID,
-                    mContext.getString(R.string.default_notification_channel_label),
+                    R.string.default_notification_channel_label,
                     r.importance);
             channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
             channel.setLockscreenVisibility(r.visibility);
@@ -465,7 +478,8 @@
         Preconditions.checkNotNull(pkg);
         Preconditions.checkNotNull(group);
         Preconditions.checkNotNull(group.getId());
-        Preconditions.checkNotNull(group.getName());
+        Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName())
+                || group.getNameResId() != 0);
         Record r = getOrCreateRecord(pkg, uid);
         if (r == null) {
             throw new IllegalArgumentException("Invalid package");
@@ -480,7 +494,8 @@
         Preconditions.checkNotNull(pkg);
         Preconditions.checkNotNull(channel);
         Preconditions.checkNotNull(channel.getId());
-        Preconditions.checkNotNull(channel.getName());
+        Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName())
+                || channel.getNameResId() != 0);
         Record r = getOrCreateRecord(pkg, uid);
         if (r == null) {
             throw new IllegalArgumentException("Invalid package");
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index bbd227e..aac04da 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -314,7 +314,8 @@
         optimizer.performDexOpt(pkg, libraryDependencies,
                 null /* ISAs */, false /* checkProfiles */,
                 getCompilerFilterForReason(compilationReason),
-                null /* CompilerStats.PackageStats */);
+                null /* CompilerStats.PackageStats */,
+                mPackageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName));
 
         return commands;
     }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index b589057..d9ea728 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -104,7 +104,7 @@
      */
     int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
             String[] instructionSets, boolean checkProfiles, String targetCompilationFilter,
-            CompilerStats.PackageStats packageStats) {
+            CompilerStats.PackageStats packageStats, boolean isUsedByOtherApps) {
         if (!canOptimizePackage(pkg)) {
             return DEX_OPT_SKIPPED;
         }
@@ -119,7 +119,7 @@
             }
             try {
                 return performDexOptLI(pkg, sharedLibraries, instructionSets, checkProfiles,
-                        targetCompilationFilter, packageStats);
+                        targetCompilationFilter, packageStats, isUsedByOtherApps);
             } finally {
                 if (useLock) {
                     mDexoptWakeLock.release();
@@ -135,7 +135,8 @@
     @GuardedBy("mInstallLock")
     private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
             String[] targetInstructionSets, boolean checkForProfileUpdates,
-            String targetCompilerFilter, CompilerStats.PackageStats packageStats) {
+            String targetCompilerFilter, CompilerStats.PackageStats packageStats,
+            boolean isUsedByOtherApps) {
         final String[] instructionSets = targetInstructionSets != null ?
                 targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
@@ -143,7 +144,7 @@
         final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
 
         final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
-                targetCompilerFilter, isUsedByOtherApps(pkg));
+                targetCompilerFilter, isUsedByOtherApps);
         final boolean profileUpdated = checkForProfileUpdates &&
                 isProfileUpdated(pkg, sharedGid, compilerFilter);
 
@@ -477,40 +478,6 @@
         mSystemReady = true;
     }
 
-    /**
-     * Returns true if the profiling data collected for the given app indicate
-     * that the apps's APK has been loaded by another app.
-     * Note that this returns false for all forward-locked apps and apps without
-     * any collected profiling data.
-     */
-    public static boolean isUsedByOtherApps(PackageParser.Package pkg) {
-        if (pkg.isForwardLocked()) {
-            // Skip the check for forward locked packages since they don't share their code.
-            return false;
-        }
-
-        for (String apkPath : pkg.getAllCodePathsExcludingResourceOnly()) {
-            try {
-                apkPath = PackageManagerServiceUtils.realpath(new File(apkPath));
-            } catch (IOException e) {
-                // Log an error but continue without it.
-                Slog.w(TAG, "Failed to get canonical path", e);
-                continue;
-            }
-            String useMarker = apkPath.replace('/', '@');
-            final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
-            for (int i = 0; i < currentUserIds.length; i++) {
-                File profileDir =
-                        Environment.getDataProfilesDeForeignDexDirectory(currentUserIds[i]);
-                File foreignUseMark = new File(profileDir, useMarker);
-                if (foreignUseMark.exists()) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     private String printDexoptFlags(int flags) {
         ArrayList<String> flagsList = new ArrayList<>();
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8380983..a4d35a1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -746,6 +746,12 @@
     @GuardedBy("mPackages")
     final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers = new SparseArray<>();
 
+    final PackageParser.Callback mPackageParserCallback = new PackageParser.Callback() {
+        @Override public boolean hasFeature(String feature) {
+            return PackageManagerService.this.hasSystemFeature(feature, 0);
+        }
+    };
+
     public static final class SharedLibraryEntry {
         public final String path;
         public final String apk;
@@ -7584,7 +7590,7 @@
                     + " flags=0x" + Integer.toHexString(parseFlags));
         }
         ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
-                mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir);
+                mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mPackageParserCallback);
 
         // Submit files for parsing in parallel
         int fileCount = 0;
@@ -7753,6 +7759,7 @@
         pp.setSeparateProcesses(mSeparateProcesses);
         pp.setOnlyCoreApps(mOnlyCore);
         pp.setDisplayMetrics(mMetrics);
+        pp.setCallback(mPackageParserCallback);
 
         if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
             parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
@@ -8434,11 +8441,13 @@
                 pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
                         false /* checkProfiles */,
                         getCompilerFilterForReason(REASON_NON_SYSTEM_LIBRARY),
-                        getOrCreateCompilerPackageStats(depPackage));
+                        getOrCreateCompilerPackageStats(depPackage),
+                        mDexManager.isUsedByOtherApps(p.packageName));
             }
         }
         return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets, checkProfiles,
-                targetCompilerFilter, getOrCreateCompilerPackageStats(p));
+                targetCompilerFilter, getOrCreateCompilerPackageStats(p),
+                mDexManager.isUsedByOtherApps(p.packageName));
     }
 
     // Performs dexopt on the used secondary dex files belonging to the given package.
@@ -8710,6 +8719,7 @@
             } catch (InstallerException e) {
                 Slog.w(TAG, String.valueOf(e));
             }
+            mDexManager.notifyPackageDataDestroyed(pkg.packageName, userId);
         }
     }
 
@@ -9616,7 +9626,7 @@
 
     /**
      * Asserts the parsed package is valid according to the given policy. If the
-     * package is invalid, for whatever reason, throws {@link PackgeManagerException}.
+     * package is invalid, for whatever reason, throws {@link PackageManagerException}.
      * <p>
      * Implementation detail: This method must NOT have any side effects. It would
      * ideally be static, but, it requires locks to read system state.
@@ -12814,7 +12824,7 @@
          * By having a field variable, we're able to track filter ordering as soon as
          * a non-zero order is defined. Otherwise, multiple loops across the result set
          * would be needed to apply ordering. If the intent resolver becomes re-entrant,
-         * this needs to be contained entirely within {@link #filterResults()}.
+         * this needs to be contained entirely within {@link #filterResults}.
          */
         final ArrayMap<String, Pair<Integer, EphemeralResolveInfo>> mOrderResult = new ArrayMap<>();
 
@@ -15909,14 +15919,6 @@
 
             final PackageSetting ps = mSettings.mPackages.get(pkgName);
 
-            // don't allow an upgrade from full to ephemeral
-            if (isInstantApp && !ps.getInstantApp(user.getIdentifier())) {
-                // can't downgrade from full to instant
-                Slog.w(TAG, "Can't replace app with instant app: " + pkgName);
-                res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
-                return;
-            }
-
             // verify signatures are valid
             if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
                 if (!checkUpgradeKeySetLP(ps, pkg)) {
@@ -15974,6 +15976,27 @@
             // In case of rollback, remember per-user/profile install state
             allUsers = sUserManager.getUserIds();
             installedUsers = ps.queryInstalledUsers(allUsers, true);
+
+            // don't allow an upgrade from full to ephemeral
+            if (isInstantApp) {
+                if (user == null || user.getIdentifier() == UserHandle.USER_ALL) {
+                    for (int currentUser : allUsers) {
+                        if (!ps.getInstantApp(currentUser)) {
+                            // can't downgrade from full to instant
+                            Slog.w(TAG, "Can't replace full app with instant app: " + pkgName
+                                    + " for user: " + currentUser);
+                            res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+                            return;
+                        }
+                    }
+                } else if (!ps.getInstantApp(user.getIdentifier())) {
+                    // can't downgrade from full to instant
+                    Slog.w(TAG, "Can't replace full app with instant app: " + pkgName
+                            + " for user: " + user.getIdentifier());
+                    res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+                    return;
+                }
+            }
         }
 
         // Update what is removed
@@ -16125,6 +16148,8 @@
                 setInstantAppForUser(ps, user.getIdentifier(), instantApp, fullApp);
                 prepareAppDataAfterInstallLIF(newPackage);
                 addedPkg = true;
+                mDexManager.notifyPackageUpdated(newPackage.packageName,
+                        newPackage.baseCodePath, newPackage.splitCodePaths);
             } catch (PackageManagerException e) {
                 res.setError("Package couldn't be installed in " + pkg.codePath, e);
             }
@@ -16274,6 +16299,9 @@
                 updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
                         installReason);
                 prepareAppDataAfterInstallLIF(newPackage);
+
+                mDexManager.notifyPackageUpdated(newPackage.packageName,
+                            newPackage.baseCodePath, newPackage.splitCodePaths);
             }
         } catch (PackageManagerException e) {
             res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
@@ -16636,6 +16664,7 @@
         PackageParser pp = new PackageParser();
         pp.setSeparateProcesses(mSeparateProcesses);
         pp.setDisplayMetrics(mMetrics);
+        pp.setCallback(mPackageParserCallback);
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
         final PackageParser.Package pkg;
@@ -16648,13 +16677,19 @@
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
 
-//        // Ephemeral apps must have target SDK >= O.
-//        // TODO: Update conditional and error message when O gets locked down
-//        if (instantApp && pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
-//            res.setError(PackageManager.INSTALL_FAILED_EPHEMERAL_INVALID,
-//                    "Ephemeral apps must have target SDK version of at least O");
-//            return;
-//        }
+        // Instant apps must have target SDK >= O and have targetSanboxVersion >= 2
+        if (instantApp && pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
+            Slog.w(TAG, "Instant app package " + pkg.packageName
+                    + " does not target O, this will be a fatal error.");
+            // STOPSHIP: Make this a fatal error
+            pkg.applicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
+        }
+        if (instantApp && pkg.applicationInfo.targetSandboxVersion != 2) {
+            Slog.w(TAG, "Instant app package " + pkg.packageName
+                    + " does not target targetSandboxVersion 2, this will be a fatal error.");
+            // STOPSHIP: Make this a fatal error
+            pkg.applicationInfo.targetSandboxVersion = 2;
+        }
 
         if (pkg.applicationInfo.isStaticSharedLibrary()) {
             // Static shared libraries have synthetic package names
@@ -16951,7 +16986,8 @@
             mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
                     null /* instructionSets */, false /* checkProfiles */,
                     getCompilerFilterForReason(REASON_INSTALL),
-                    getOrCreateCompilerPackageStats(pkg));
+                    getOrCreateCompilerPackageStats(pkg),
+                    mDexManager.isUsedByOtherApps(pkg.packageName));
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
             // Notify BackgroundDexOptJobService that the package has been changed.
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 9c9a671..0634dac 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -80,9 +80,7 @@
             try {
                 // Check that the system property name is legal.
                 String sysPropName = getSystemPropertyName(reason);
-                if (sysPropName == null ||
-                        sysPropName.isEmpty() ||
-                        sysPropName.length() > SystemProperties.PROP_NAME_MAX) {
+                if (sysPropName == null || sysPropName.isEmpty()) {
                     throw new IllegalStateException("Reason system property name \"" +
                             sysPropName +"\" for reason " + REASON_STRINGS[reason]);
                 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 45887e1..9feee8c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -133,7 +133,8 @@
                 sortTemp, packageManagerService);
 
         // Give priority to apps used by other apps.
-        applyPackageFilter((pkg) -> PackageDexOptimizer.isUsedByOtherApps(pkg), result,
+        applyPackageFilter((pkg) ->
+                packageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName), result,
                 remainingPkgs, sortTemp, packageManagerService);
 
         // Filter out packages that aren't recently used, add all remaining apps.
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index 6033855..4ff3e12 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -46,6 +46,7 @@
     private final boolean mOnlyCore;
     private final DisplayMetrics mMetrics;
     private final File mCacheDir;
+    private final PackageParser.Callback mPackageParserCallback;
     private volatile String mInterruptedInThread;
 
     private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
@@ -54,11 +55,12 @@
             "package-parsing-thread", Process.THREAD_PRIORITY_FOREGROUND);
 
     ParallelPackageParser(String[] separateProcesses, boolean onlyCoreApps,
-            DisplayMetrics metrics, File cacheDir) {
+            DisplayMetrics metrics, File cacheDir, PackageParser.Callback callback) {
         mSeparateProcesses = separateProcesses;
         mOnlyCore = onlyCoreApps;
         mMetrics = metrics;
         mCacheDir = cacheDir;
+        mPackageParserCallback = callback;
     }
 
     static class ParseResult {
@@ -110,6 +112,7 @@
                 pp.setOnlyCoreApps(mOnlyCore);
                 pp.setDisplayMetrics(mMetrics);
                 pp.setCacheDir(mCacheDir);
+                pp.setCallback(mPackageParserCallback);
                 pr.scanFile = scanFile;
                 pr.pkg = parsePackage(pp, scanFile, parseFlags);
             } catch (Throwable e) {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index ac98ab9..7885748 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -1635,10 +1635,10 @@
                 Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
                         + " still has an icon");
             }
-            if (si.hasMaskableBitmap() && !si.hasIconFile()) {
+            if (si.hasAdaptiveBitmap() && !si.hasIconFile()) {
                 failed = true;
                 Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
-                    + " has maskable bitmap but was not saved to a file.");
+                    + " has adaptive bitmap but was not saved to a file.");
             }
             if (si.hasIconFile() && si.hasIconResource()) {
                 failed = true;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 74eb340..43288cd 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -46,7 +46,6 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
-import android.content.pm.ShortcutManager;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
 import android.content.pm.UserInfo;
@@ -1219,7 +1218,7 @@
         shortcut.setIconResourceId(0);
         shortcut.setIconResName(null);
         shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE |
-            ShortcutInfo.FLAG_MASKABLE_BITMAP | ShortcutInfo.FLAG_HAS_ICON_RES);
+            ShortcutInfo.FLAG_ADAPTIVE_BITMAP | ShortcutInfo.FLAG_HAS_ICON_RES);
     }
 
     public void cleanupBitmapsForPackage(@UserIdInt int userId, String packageName) {
@@ -1355,7 +1354,7 @@
                         return;
                     }
                     case Icon.TYPE_BITMAP:
-                    case Icon.TYPE_BITMAP_MASKABLE: {
+                    case Icon.TYPE_ADAPTIVE_BITMAP: {
                         bitmap = icon.getBitmap(); // Don't recycle in this case.
                         break;
                     }
@@ -1386,8 +1385,8 @@
 
                         shortcut.setBitmapPath(out.getFile().getAbsolutePath());
                         shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE);
-                        if (icon.getType() == Icon.TYPE_BITMAP_MASKABLE) {
-                            shortcut.addFlags(ShortcutInfo.FLAG_MASKABLE_BITMAP);
+                        if (icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) {
+                            shortcut.addFlags(ShortcutInfo.FLAG_ADAPTIVE_BITMAP);
                         }
                     } finally {
                         IoUtils.closeQuietly(out);
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 00f3711..755c486 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -22,6 +22,7 @@
 import android.content.pm.PackageParser;
 import android.os.RemoteException;
 import android.os.storage.StorageManager;
+import android.os.UserHandle;
 
 import android.util.Slog;
 
@@ -179,17 +180,64 @@
         }
     }
 
-    public void notifyPackageInstalled(PackageInfo info, int userId) {
-        cachePackageCodeLocation(info, userId);
+    /**
+     * Notifies that a new package was installed for {@code userId}.
+     * {@code userId} must not be {@code UserHandle.USER_ALL}.
+     *
+     * @throws IllegalArgumentException if {@code userId} is {@code UserHandle.USER_ALL}.
+     */
+    public void notifyPackageInstalled(PackageInfo pi, int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            throw new IllegalArgumentException(
+                "notifyPackageInstalled called with USER_ALL");
+        }
+        cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir,
+                pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId);
     }
 
-    private void cachePackageCodeLocation(PackageInfo info, int userId) {
-        PackageCodeLocations pcl = mPackageCodeLocationsCache.get(info.packageName);
-        if (pcl != null) {
-            pcl.mergeAppDataDirs(info.applicationInfo, userId);
-        } else {
-            mPackageCodeLocationsCache.put(info.packageName,
-                new PackageCodeLocations(info.applicationInfo, userId));
+    /**
+     * Notifies that package {@code packageName} was updated.
+     * This will clear the UsedByOtherApps mark if it exists.
+     */
+    public void notifyPackageUpdated(String packageName, String baseCodePath,
+            String[] splitCodePaths) {
+        cachePackageCodeLocation(packageName, baseCodePath, splitCodePaths, null, /*userId*/ -1);
+        // In case there was an update, write the package use info to disk async.
+        // Note that we do the writing here and not in PackageDexUsage in order to be
+        // consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs
+        // multiple updates in PackaeDexUsage before writing it).
+        if (mPackageDexUsage.clearUsedByOtherApps(packageName)) {
+            mPackageDexUsage.maybeWriteAsync();
+        }
+    }
+
+    /**
+     * Notifies that the user {@code userId} data for package {@code packageName}
+     * was destroyed. This will remove all usage info associated with the package
+     * for the given user.
+     * {@code userId} is allowed to be {@code UserHandle.USER_ALL} in which case
+     * all usage information for the package will be removed.
+     */
+    public void notifyPackageDataDestroyed(String packageName, int userId) {
+        boolean updated = userId == UserHandle.USER_ALL
+            ? mPackageDexUsage.removePackage(packageName)
+            : mPackageDexUsage.removeUserPackage(packageName, userId);
+        // In case there was an update, write the package use info to disk async.
+        // Note that we do the writing here and not in PackageDexUsage in order to be
+        // consistent with other methods in DexManager (e.g. reconcileSecondaryDexFiles performs
+        // multiple updates in PackaeDexUsage before writing it).
+        if (updated) {
+            mPackageDexUsage.maybeWriteAsync();
+        }
+    }
+
+    public void cachePackageCodeLocation(String packageName, String baseCodePath,
+            String[] splitCodePaths, String dataDir, int userId) {
+        PackageCodeLocations pcl = putIfAbsent(mPackageCodeLocationsCache, packageName,
+                new PackageCodeLocations(packageName, baseCodePath, splitCodePaths));
+        pcl.updateCodeLocation(baseCodePath, splitCodePaths);
+        if (dataDir != null) {
+            pcl.mergeAppDataDirs(dataDir, userId);
         }
     }
 
@@ -202,7 +250,8 @@
             int userId = entry.getKey();
             for (PackageInfo pi : packageInfoList) {
                 // Cache the code locations.
-                cachePackageCodeLocation(pi, userId);
+                cachePackageCodeLocation(pi.packageName, pi.applicationInfo.sourceDir,
+                        pi.applicationInfo.splitSourceDirs, pi.applicationInfo.dataDir, userId);
 
                 // Cache a map from package name to the set of user ids who installed the package.
                 // We will use it to sync the data and remove obsolete entries from
@@ -359,6 +408,23 @@
     }
 
     /**
+     * Return true if the profiling data collected for the given app indicate
+     * that the apps's APK has been loaded by another app.
+     * Note that this returns false for all apps without any collected profiling data.
+    */
+    public boolean isUsedByOtherApps(String packageName) {
+        PackageUseInfo useInfo = getPackageUseInfo(packageName);
+        if (useInfo == null) {
+            // No use info, means the package was not used or it was used but not by other apps.
+            // Note that right now we might prune packages which are not used by other apps.
+            // TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
+            // to access the package use.
+            return false;
+        }
+        return useInfo.isUsedByOtherApps();
+    }
+
+    /**
      * Retrieves the package which owns the given dexPath.
      */
     private DexSearchResult getDexPackage(
@@ -408,27 +474,36 @@
      */
     private static class PackageCodeLocations {
         private final String mPackageName;
-        private final String mBaseCodePath;
+        private String mBaseCodePath;
         private final Set<String> mSplitCodePaths;
         // Maps user id to the application private directory.
         private final Map<Integer, Set<String>> mAppDataDirs;
 
         public PackageCodeLocations(ApplicationInfo ai, int userId) {
-            mPackageName = ai.packageName;
-            mBaseCodePath = ai.sourceDir;
+            this(ai.packageName, ai.sourceDir, ai.splitSourceDirs);
+            mergeAppDataDirs(ai.dataDir, userId);
+        }
+        public PackageCodeLocations(String packageName, String baseCodePath,
+                String[] splitCodePaths) {
+            mPackageName = packageName;
             mSplitCodePaths = new HashSet<>();
-            if (ai.splitSourceDirs != null) {
-                for (String split : ai.splitSourceDirs) {
+            mAppDataDirs = new HashMap<>();
+            updateCodeLocation(baseCodePath, splitCodePaths);
+        }
+
+        public void updateCodeLocation(String baseCodePath, String[] splitCodePaths) {
+            mBaseCodePath = baseCodePath;
+            mSplitCodePaths.clear();
+            if (splitCodePaths != null) {
+                for (String split : splitCodePaths) {
                     mSplitCodePaths.add(split);
                 }
             }
-            mAppDataDirs = new HashMap<>();
-            mergeAppDataDirs(ai, userId);
         }
 
-        public void mergeAppDataDirs(ApplicationInfo ai, int userId) {
+        public void mergeAppDataDirs(String dataDir, int userId) {
             Set<String> dataDirs = putIfAbsent(mAppDataDirs, userId, new HashSet<>());
-            dataDirs.add(ai.dataDir);
+            dataDirs.add(dataDir);
         }
 
         public int searchDex(String dexPath, int userId) {
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 3693bce0..8a66f12 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -377,7 +377,34 @@
     }
 
     /**
+     * Clears the {@code usesByOtherApps} marker for the package {@code packageName}.
+     * @return true if the package usage info was updated.
+     */
+    public boolean clearUsedByOtherApps(String packageName) {
+        synchronized (mPackageUseInfoMap) {
+            PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
+            if (packageUseInfo == null || !packageUseInfo.mIsUsedByOtherApps) {
+                return false;
+            }
+            packageUseInfo.mIsUsedByOtherApps = false;
+            return true;
+        }
+    }
+
+    /**
+     * Remove the usage data associated with package {@code packageName}.
+     * @return true if the package usage was found and removed successfully.
+     */
+    public boolean removePackage(String packageName) {
+        synchronized (mPackageUseInfoMap) {
+            return mPackageUseInfoMap.remove(packageName) != null;
+        }
+    }
+
+    /**
      * Remove all the records about package {@code packageName} belonging to user {@code userId}.
+     * If the package is left with no records of secondary dex usage and is not used by other
+     * apps it will be removed as well.
      * @return true if the record was found and actually deleted,
      *         false if the record doesn't exist
      */
@@ -397,6 +424,12 @@
                     updated = true;
                 }
             }
+            // If no secondary dex info is left and the package is not used by other apps
+            // remove the data since it is now useless.
+            if (packageUseInfo.mDexUseInfoMap.isEmpty() && !packageUseInfo.mIsUsedByOtherApps) {
+                mPackageUseInfoMap.remove(packageName);
+                updated = true;
+            }
             return updated;
         }
     }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0d6cd80..4f1754a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2398,8 +2398,8 @@
              * permission to add alert windows that aren't
              * {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY}.
              */
-            return (mContext.checkCallingPermission(INTERNAL_SYSTEM_WINDOW) == PERMISSION_GRANTED)
-                    ? ADD_OKAY : ADD_PERMISSION_DENIED;
+            return (mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
+                    == PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
         }
 
         // check if user has enabled this operation. SecurityException will be thrown if this app
@@ -2420,8 +2420,8 @@
             default:
                 // in the default mode, we will make a decision here based on
                 // checkCallingPermission()
-                return (mContext.checkCallingPermission(SYSTEM_ALERT_WINDOW) == PERMISSION_GRANTED)
-                        ? ADD_OKAY : ADD_PERMISSION_DENIED;
+                return (mContext.checkCallingOrSelfPermission(SYSTEM_ALERT_WINDOW)
+                        == PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
         }
     }
 
diff --git a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
index 10d30aa..c064392 100644
--- a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
+++ b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
@@ -34,21 +34,37 @@
 import android.content.pm.UserInfo;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.IBinder;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.format.DateUtils;
+import android.util.Pair;
 import android.util.Slog;
+import android.util.Xml;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.AtomicFile;
+import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
 import com.android.server.pm.Installer;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 
-
 /**
  * CacheQuotaStrategy is a strategy for determining cache quotas using usage stats and foreground
  * time using the calculation as defined in the refuel rocket.
@@ -58,17 +74,28 @@
 
     private final Object mLock = new Object();
 
+    // XML Constants
+    private static final String CACHE_INFO_TAG = "cache-info";
+    private static final String ATTR_PREVIOUS_BYTES = "previousBytes";
+    private static final String TAG_QUOTA = "quota";
+    private static final String ATTR_UUID = "uuid";
+    private static final String ATTR_UID = "uid";
+    private static final String ATTR_QUOTA_IN_BYTES = "bytes";
+
     private final Context mContext;
     private final UsageStatsManagerInternal mUsageStats;
     private final Installer mInstaller;
     private ServiceConnection mServiceConnection;
     private ICacheQuotaService mRemoteService;
+    private AtomicFile mPreviousValuesFile;
 
     public CacheQuotaStrategy(
             Context context, UsageStatsManagerInternal usageStatsManager, Installer installer) {
         mContext = Preconditions.checkNotNull(context);
         mUsageStats = Preconditions.checkNotNull(usageStatsManager);
         mInstaller = Preconditions.checkNotNull(installer);
+        mPreviousValuesFile = new AtomicFile(new File(
+                new File(Environment.getDataDirectory(), "system"), "cachequota.xml"));
     }
 
     /**
@@ -128,7 +155,7 @@
     }
 
     /**
-     * Returns a list of CacheQuotaRequests which do not have their quotas filled out for apps
+     * Returns a list of CacheQuotaHints which do not have their quotas filled out for apps
      * which have been used in the last year.
      */
     private List<CacheQuotaHint> getUnfulfilledRequests() {
@@ -176,6 +203,11 @@
         final List<CacheQuotaHint> processedRequests =
                 data.getParcelableArrayList(
                         CacheQuotaService.REQUEST_LIST_KEY);
+        pushProcessedQuotas(processedRequests);
+        writeXmlToFile(processedRequests);
+    }
+
+    private void pushProcessedQuotas(List<CacheQuotaHint> processedRequests) {
         final int requestSize = processedRequests.size();
         for (int i = 0; i < requestSize; i++) {
             CacheQuotaHint request = processedRequests.get(i);
@@ -200,8 +232,10 @@
     }
 
     private void disconnectService() {
-        mContext.unbindService(mServiceConnection);
-        mServiceConnection = null;
+        if (mServiceConnection != null) {
+            mContext.unbindService(mServiceConnection);
+            mServiceConnection = null;
+        }
     }
 
     private ComponentName getServiceComponentName() {
@@ -223,4 +257,131 @@
         ServiceInfo serviceInfo = resolveInfo.serviceInfo;
         return new ComponentName(serviceInfo.packageName, serviceInfo.name);
     }
+
+    private void writeXmlToFile(List<CacheQuotaHint> processedRequests) {
+        FileOutputStream fileStream = null;
+        try {
+            XmlSerializer out = new FastXmlSerializer();
+            fileStream = mPreviousValuesFile.startWrite();
+            out.setOutput(fileStream, StandardCharsets.UTF_8.name());
+            saveToXml(out, processedRequests, 0);
+            mPreviousValuesFile.finishWrite(fileStream);
+        } catch (Exception e) {
+            Slog.e(TAG, "An error occurred while writing the cache quota file.", e);
+            mPreviousValuesFile.failWrite(fileStream);
+        }
+    }
+
+    /**
+     * Initializes the quotas from the file.
+     * @return the number of bytes that were free on the device when the quotas were last calced.
+     */
+    public long setupQuotasFromFile() throws IOException {
+        FileInputStream stream;
+        try {
+            stream = mPreviousValuesFile.openRead();
+        } catch (FileNotFoundException e) {
+            // The file may not exist yet -- this isn't truly exceptional.
+            return -1;
+        }
+
+        Pair<Long, List<CacheQuotaHint>> cachedValues = null;
+        try {
+            cachedValues = readFromXml(stream);
+        } catch (XmlPullParserException e) {
+            throw new IllegalStateException(e.getMessage());
+        }
+
+        if (cachedValues == null) {
+            Slog.e(TAG, "An error occurred while parsing the cache quota file.");
+            return -1;
+        }
+        pushProcessedQuotas(cachedValues.second);
+        return cachedValues.first;
+    }
+
+    @VisibleForTesting
+    static void saveToXml(XmlSerializer out,
+            List<CacheQuotaHint> requests, long bytesWhenCalculated) throws IOException {
+        out.startDocument(null, true);
+        out.startTag(null, CACHE_INFO_TAG);
+        int requestSize = requests.size();
+        out.attribute(null, ATTR_PREVIOUS_BYTES, Long.toString(bytesWhenCalculated));
+
+        for (int i = 0; i < requestSize; i++) {
+            CacheQuotaHint request = requests.get(i);
+            out.startTag(null, TAG_QUOTA);
+            String uuid = request.getVolumeUuid();
+            if (uuid != null) {
+                out.attribute(null, ATTR_UUID, request.getVolumeUuid());
+            }
+            out.attribute(null, ATTR_UID, Integer.toString(request.getUid()));
+            out.attribute(null, ATTR_QUOTA_IN_BYTES, Long.toString(request.getQuota()));
+            out.endTag(null, TAG_QUOTA);
+        }
+        out.endTag(null, CACHE_INFO_TAG);
+        out.endDocument();
+    }
+
+    protected static Pair<Long, List<CacheQuotaHint>> readFromXml(InputStream inputStream)
+            throws XmlPullParserException, IOException {
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(inputStream, StandardCharsets.UTF_8.name());
+
+        int eventType = parser.getEventType();
+        while (eventType != XmlPullParser.START_TAG &&
+                eventType != XmlPullParser.END_DOCUMENT) {
+            eventType = parser.next();
+        }
+
+        if (eventType == XmlPullParser.END_DOCUMENT) {
+            Slog.d(TAG, "No quotas found in quota file.");
+            return null;
+        }
+
+        String tagName = parser.getName();
+        if (!CACHE_INFO_TAG.equals(tagName)) {
+            throw new IllegalStateException("Invalid starting tag.");
+        }
+
+        final List<CacheQuotaHint> quotas = new ArrayList<>();
+        long previousBytes;
+        try {
+            previousBytes = Long.parseLong(parser.getAttributeValue(
+                    null, ATTR_PREVIOUS_BYTES));
+        } catch (NumberFormatException e) {
+            throw new IllegalStateException(
+                    "Previous bytes formatted incorrectly; aborting quota read.");
+        }
+
+        eventType = parser.next();
+        do {
+            if (eventType == XmlPullParser.START_TAG) {
+                tagName = parser.getName();
+                if (TAG_QUOTA.equals(tagName)) {
+                    CacheQuotaHint request = getRequestFromXml(parser);
+                    if (request == null) {
+                        continue;
+                    }
+                    quotas.add(request);
+                }
+            }
+            eventType = parser.next();
+        } while (eventType != XmlPullParser.END_DOCUMENT);
+        return new Pair<>(previousBytes, quotas);
+    }
+
+    @VisibleForTesting
+    static CacheQuotaHint getRequestFromXml(XmlPullParser parser) {
+        try {
+            String uuid = parser.getAttributeValue(null, ATTR_UUID);
+            int uid = Integer.parseInt(parser.getAttributeValue(null, ATTR_UID));
+            long bytes = Long.parseLong(parser.getAttributeValue(null, ATTR_QUOTA_IN_BYTES));
+            return new CacheQuotaHint.Builder()
+                    .setVolumeUuid(uuid).setUid(uid).setQuota(bytes).build();
+        } catch (NumberFormatException e) {
+            Slog.e(TAG, "Invalid cache quota request, skipping.");
+            return null;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index 0d282ef..b7b419b 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.Notification.VISIBILITY_PRIVATE;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
 import static android.content.Context.NOTIFICATION_SERVICE;
@@ -34,8 +33,10 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+
 import com.android.internal.R;
+import com.android.server.policy.IconUtilities;
 
 /** Displays an ongoing notification for a process displaying an alert window */
 class AlertWindowNotification {
@@ -50,6 +51,7 @@
     private final String mPackageName;
     private final int mUid;
     private boolean mCancelled;
+    private IconUtilities mIconUtilities;
 
     AlertWindowNotification(WindowManagerService service, String packageName, int uid) {
         mService = service;
@@ -59,20 +61,33 @@
                 (NotificationManager) mService.mContext.getSystemService(NOTIFICATION_SERVICE);
         mNotificationTag = CHANNEL_PREFIX + mPackageName;
         mRequestCode = sNextRequestCode++;
+        mIconUtilities = new IconUtilities(mService.mContext);
 
         // We can't create/post the notification while the window manager lock is held since it will
         // end up calling into activity manager. So, we post a message to do it later.
-        mService.mH.post(this::postNotification);
+        mService.mH.post(this::onPostNotification);
     }
 
     /** Cancels the notification */
     void cancel() {
+        // We can't call into NotificationManager with WM lock held since it might call into AM.
+        // So, we post a message to do it later.
+        mService.mH.post(this::onCancelNotification);
+    }
+
+    /** Don't call with the window manager lock held! */
+    private void onCancelNotification() {
         mNotificationManager.cancel(mNotificationTag, NOTIFICATION_ID);
         mCancelled = true;
     }
 
     /** Don't call with the window manager lock held! */
-    private void postNotification() {
+    private void onPostNotification() {
+        if (mCancelled) {
+            // Notification was cancelled, so nothing more to do...
+            return;
+        }
+
         final Context context = mService.mContext;
         final PackageManager pm = context.getPackageManager();
         final ApplicationInfo aInfo = getApplicationInfo(pm, mPackageName);
@@ -94,17 +109,14 @@
                 .addAction(getTurnOffAction(context, mPackageName, mUid));
 
         if (aInfo != null) {
-            final Bitmap bitmap = ((BitmapDrawable) pm.getApplicationIcon(aInfo)).getBitmap();
-            builder.setLargeIcon(bitmap);
+            final Drawable drawable = pm.getApplicationIcon(aInfo);
+            if (drawable != null) {
+                final Bitmap bitmap = mIconUtilities.createIconBitmap(drawable);
+                builder.setLargeIcon(bitmap);
+            }
         }
 
-        synchronized (mService.mWindowMap) {
-            if (mCancelled) {
-                // Notification was cancelled, so nothing more to do...
-                return;
-            }
-            mNotificationManager.notify(mNotificationTag, NOTIFICATION_ID, builder.build());
-        }
+        mNotificationManager.notify(mNotificationTag, NOTIFICATION_ID, builder.build());
     }
 
     private Notification.Action getTurnOffAction(Context context, String packageName, int uid) {
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 3a86874..266ab4c 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -525,7 +525,8 @@
 
     private boolean createSnapshot() {
         final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot(
-                mContainer.mTask.mTaskId, mContainer.mTask.mUserId, false /* restoreFromDisk */);
+                mContainer.getTask().mTaskId, mContainer.getTask().mUserId,
+            false /* restoreFromDisk */);
 
         if (snapshot == null) {
             return false;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index e67c91e..647adbf 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -84,14 +84,16 @@
 
     final boolean mVoiceInteraction;
 
-    // TODO: Use getParent instead?
-    Task mTask;
     /** @see WindowContainer#fillsParent() */
     private boolean mFillsParent;
     boolean layoutConfigChanges;
     boolean mShowForAllUsers;
     int mTargetSdk;
 
+    // Flag set while reparenting to prevent actions normally triggered by an individual parent
+    // change.
+    private boolean mReparenting;
+
     // The input dispatching timeout for this application token in nanoseconds.
     long mInputDispatchingTimeoutNanos;
 
@@ -407,7 +409,7 @@
     }
 
     boolean windowsAreFocusable() {
-        return StackId.canReceiveKeys(mTask.mStack.mStackId) || mAlwaysFocusable;
+        return StackId.canReceiveKeys(getTask().mStack.mStackId) || mAlwaysFocusable;
     }
 
     AppWindowContainerController getController() {
@@ -426,10 +428,7 @@
     void removeIfPossible() {
         mIsExiting = false;
         removeAllWindowsIfPossible();
-        if (mTask != null) {
-            mTask.mStack.mExitingAppTokens.remove(this);
-            removeImmediately();
-        }
+        removeImmediately();
     }
 
     @Override
@@ -466,7 +465,7 @@
             getController().removeStartingWindow();
         }
 
-        final TaskStack stack = mTask.mStack;
+        final TaskStack stack = getTask().mStack;
         if (delayed && !isEmpty()) {
             // set the token aside because it has an active animation to be finished
             if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM,
@@ -483,6 +482,7 @@
 
         removed = true;
         stopFreezingScreen(true, true);
+
         if (mService.mFocusedApp == this) {
             if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this);
             mService.mFocusedApp = null;
@@ -664,6 +664,29 @@
         allDrawnExcludingSaved = false;
     }
 
+    Task getTask() {
+        return (Task) getParent();
+    }
+
+    @Override
+    void onParentSet() {
+        super.onParentSet();
+
+        // When the associated task is {@code null}, the {@link AppWindowToken} can no longer
+        // access visual elements like the {@link DisplayContent}. We must remove any associations
+        // such as animations.
+        if (!mReparenting) {
+            final Task task = getTask();
+            if (task == null) {
+                // It is possible we have been marked as a closing app earlier. We must remove ourselves
+                // from this list so we do not participate in any future animations.
+                mService.mClosingApps.remove(this);
+            } else if (task.mStack != null) {
+                task.mStack.mExitingAppTokens.remove(this);
+            }
+        }
+    }
+
     void postWindowRemoveStartingWindowCleanup(WindowState win) {
         // TODO: Something smells about the code below...Is there a better way?
         if (startingWindow == win) {
@@ -762,16 +785,6 @@
         if (canFreezeBounds()) {
             freezeBounds();
         }
-
-        // In the process of tearing down before relaunching, the app will
-        // try and clean up it's child surfaces. We need to prevent this from
-        // happening, so we sever the children, transfering their ownership
-        // from the client it-self to the parent surface (owned by us).
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState w = mChildren.get(i);
-            w.mWinAnimator.detachChildren();
-        }
-
         mPendingRelaunchCount++;
     }
 
@@ -862,17 +875,29 @@
     }
 
     void reparent(Task task, int position) {
-        if (task == mTask) {
+        final Task currentTask = getTask();
+        if (task == currentTask) {
             throw new IllegalArgumentException(
-                    "window token=" + this + " already child of task=" + mTask);
+                    "window token=" + this + " already child of task=" + currentTask);
         }
+
+        if (currentTask.mStack != task.mStack) {
+            throw new IllegalArgumentException(
+                    "window token=" + this + " current task=" + currentTask
+                        + " belongs to a different stack than " + task);
+        }
+
         if (DEBUG_ADD_REMOVE) Slog.i(TAG, "reParentWindowToken: removing window token=" + this
-                + " from task=" + mTask);
+                + " from task=" + currentTask);
         final DisplayContent prevDisplayContent = getDisplayContent();
 
+        mReparenting = true;
+
         getParent().removeChild(this);
         task.addChild(this, position);
 
+        mReparenting = false;
+
         // Relayout display(s).
         final DisplayContent displayContent = task.getDisplayContent();
         displayContent.setLayoutNeeded();
@@ -883,9 +908,11 @@
     }
 
     private boolean canFreezeBounds() {
+        final Task task = getTask();
+
         // For freeform windows, we can't freeze the bounds at the moment because this would make
         // the resizing unresponsive.
-        return mTask != null && !mTask.inFreeformWorkspace();
+        return task != null && !task.inFreeformWorkspace();
     }
 
     /**
@@ -895,16 +922,17 @@
      * with a queue.
      */
     private void freezeBounds() {
-        mFrozenBounds.offer(new Rect(mTask.mPreparedFrozenBounds));
+        final Task task = getTask();
+        mFrozenBounds.offer(new Rect(task.mPreparedFrozenBounds));
 
-        if (mTask.mPreparedFrozenMergedConfig.equals(Configuration.EMPTY)) {
+        if (task.mPreparedFrozenMergedConfig.equals(Configuration.EMPTY)) {
             // We didn't call prepareFreezingBounds on the task, so use the current value.
-            mFrozenMergedConfig.offer(new Configuration(mTask.getConfiguration()));
+            mFrozenMergedConfig.offer(new Configuration(task.getConfiguration()));
         } else {
-            mFrozenMergedConfig.offer(new Configuration(mTask.mPreparedFrozenMergedConfig));
+            mFrozenMergedConfig.offer(new Configuration(task.mPreparedFrozenMergedConfig));
         }
         // Calling unset() to make it equal to Configuration.EMPTY.
-        mTask.mPreparedFrozenMergedConfig.unset();
+        task.mPreparedFrozenMergedConfig.unset();
     }
 
     /**
@@ -1429,7 +1457,7 @@
         if (appToken != null) {
             pw.println(prefix + "app=true mVoiceInteraction=" + mVoiceInteraction);
         }
-        pw.print(prefix); pw.print("task="); pw.println(mTask);
+        pw.print(prefix); pw.print("task="); pw.println(getTask());
         pw.print(prefix); pw.print(" mFillsParent="); pw.print(mFillsParent);
                 pw.print(" mOrientation="); pw.println(mOrientation);
         pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 75a79fd..aa8e3774 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -588,7 +588,7 @@
     private boolean containsAppInDockedStack(ArraySet<AppWindowToken> apps) {
         for (int i = apps.size() - 1; i >= 0; i--) {
             final AppWindowToken token = apps.valueAt(i);
-            if (token.mTask != null && token.mTask.mStack.mStackId == DOCKED_STACK_ID) {
+            if (token.getTask() != null && token.getTask().mStack.mStackId == DOCKED_STACK_ID) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 5355f31..720a454 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -93,7 +93,7 @@
         mUid = Binder.getCallingUid();
         mPid = Binder.getCallingPid();
         mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
-        mCanAddInternalSystemWindow = service.mContext.checkCallingPermission(
+        mCanAddInternalSystemWindow = service.mContext.checkCallingOrSelfPermission(
                 INTERNAL_SYSTEM_WINDOW) == PERMISSION_GRANTED;
         StringBuilder sb = new StringBuilder();
         sb.append("Session{");
@@ -664,6 +664,7 @@
 
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mNumWindow="); pw.print(mNumWindow);
+                pw.print(" mCanAddInternalSystemWindow="); pw.print(mCanAddInternalSystemWindow);
                 pw.print(" mAppOverlaySurfaces="); pw.print(mAppOverlaySurfaces);
                 pw.print(" mAlertWindowSurfaces="); pw.print(mAlertWindowSurfaces);
                 pw.print(" mClientDead="); pw.print(mClientDead);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index da5fcf3..99c085f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -133,7 +133,6 @@
     void addChild(AppWindowToken wtoken, int position) {
         position = getAdjustedAddPosition(position);
         super.addChild(wtoken, position);
-        wtoken.mTask = this;
         mDeferRemoval = false;
     }
 
@@ -244,7 +243,6 @@
                 removeIfPossible();
             }
         }
-        token.mTask = null;
     }
 
     void setSendingToBottom(boolean toBottom) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index 601bf28..4028336 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -106,8 +106,8 @@
         if (taskId != null) {
             removeRunningEntry(taskId);
         }
-        if (wtoken.mTask != null) {
-            mRetrievalCache.remove(wtoken.mTask.mTaskId);
+        if (wtoken.getTask() != null) {
+            mRetrievalCache.remove(wtoken.getTask().mTaskId);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 5041138..5995bba 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -141,11 +141,12 @@
         outClosingTasks.clear();
         for (int i = closingApps.size() - 1; i >= 0; i--) {
             final AppWindowToken atoken = closingApps.valueAt(i);
+            final Task task = atoken.getTask();
 
             // If the task of the app is not visible anymore, it means no other app in that task
             // is opening. Thus, the task is closing.
-            if (atoken.mTask != null && !atoken.mTask.isVisible()) {
-                outClosingTasks.add(closingApps.valueAt(i).mTask);
+            if (task != null && !task.isVisible()) {
+                outClosingTasks.add(task);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 9f52412..9f34bd7 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -96,9 +96,11 @@
             // TODO: Inherit behavior whether to draw behind status bar/nav bar.
             layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                     | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
-            layoutParams.setTitle(String.format(TITLE_FORMAT, token.mTask.mTaskId));
-            if (token.mTask != null) {
-                final TaskDescription taskDescription = token.mTask.getTaskDescription();
+            final Task task = token.getTask();
+            if (task != null) {
+                layoutParams.setTitle(String.format(TITLE_FORMAT,task.mTaskId));
+
+                final TaskDescription taskDescription = task.getTaskDescription();
                 if (taskDescription != null) {
                     fillBackgroundColor = taskDescription.getBackgroundColor();
                 }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index b79ea71..7588b71 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -644,7 +644,7 @@
         }
         for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
             final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
-            if (wtoken.mTask == task) {
+            if (wtoken.getTask() == task) {
                 wtoken.mIsExiting = false;
                 mExitingAppTokens.remove(appNdx);
             }
@@ -1439,6 +1439,14 @@
             mBoundsAnimating = true;
             mBoundsAnimatingToFullscreen = toFullscreen;
         }
+
+        if (mStackId == PINNED_STACK_ID) {
+            try {
+                mService.mActivityManager.notifyPinnedStackAnimationStarted();
+            } catch (RemoteException e) {
+                // I don't believe you...
+            }
+        }
     }
 
     @Override  // AnimatesBounds
@@ -1448,6 +1456,7 @@
             mBoundsAnimationTarget.setEmpty();
             mService.requestTraversal();
         }
+
         if (mStackId == PINNED_STACK_ID) {
             try {
                 mService.mActivityManager.notifyPinnedStackAnimationEnded();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index eb10f0c..e339329 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1445,9 +1445,9 @@
             if (displayContent.isDefaultDisplay) {
                 final DisplayInfo displayInfo = displayContent.getDisplayInfo();
                 final Rect taskBounds;
-                if (atoken != null && atoken.mTask != null) {
+                if (atoken != null && atoken.getTask() != null) {
                     taskBounds = mTmpRect;
-                    atoken.mTask.getBounds(mTmpRect);
+                    atoken.getTask().getBounds(mTmpRect);
                 } else {
                     taskBounds = null;
                 }
@@ -2199,15 +2199,6 @@
         if (mAccessibilityController != null && win.getDisplayId() == DEFAULT_DISPLAY) {
             mAccessibilityController.onWindowTransitionLocked(win, transit);
         }
-
-        // When we start the exit animation we take the Surface from the client
-        // so it will stop perturbing it. We need to likewise takeaway the SurfaceFlinger
-        // side child surfaces, so they will remain preserved in their current state
-        // (rather than be cleaned up immediately by the app code).
-        SurfaceControl.openTransaction();
-        winAnimator.detachChildren();
-        SurfaceControl.closeTransaction();
-
         return focusMayChange;
     }
 
@@ -2292,7 +2283,7 @@
         // is running.
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WM#applyAnimationLocked");
         if (okToDisplay()) {
-            final DisplayContent displayContent = atoken.mTask.getDisplayContent();
+            final DisplayContent displayContent = atoken.getTask().getDisplayContent();
             final DisplayInfo displayInfo = displayContent.getDisplayInfo();
             final int width = displayInfo.appWidth;
             final int height = displayInfo.appHeight;
@@ -2333,7 +2324,7 @@
             final Configuration displayConfig = displayContent.getConfiguration();
             Animation a = mAppTransition.loadAnimation(lp, transit, enter, displayConfig.uiMode,
                     displayConfig.orientation, frame, displayFrame, insets, surfaceInsets,
-                    isVoiceInteraction, freeform, atoken.mTask.mTaskId);
+                    isVoiceInteraction, freeform, atoken.getTask().mTaskId);
             if (a != null) {
                 if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + atoken);
                 final int containingWidth = frame.width();
@@ -2553,8 +2544,8 @@
     }
 
     void setFocusTaskRegionLocked(AppWindowToken previousFocus) {
-        final Task focusedTask = mFocusedApp != null ? mFocusedApp.mTask : null;
-        final Task previousTask = previousFocus != null ? previousFocus.mTask : null;
+        final Task focusedTask = mFocusedApp != null ? mFocusedApp.getTask() : null;
+        final Task previousTask = previousFocus != null ? previousFocus.getTask() : null;
         final DisplayContent focusedDisplayContent =
                 focusedTask != null ? focusedTask.getDisplayContent() : null;
         final DisplayContent previousDisplayContent =
@@ -5085,8 +5076,8 @@
         // Also don't use mInputMethodTarget's stack, because some window with FLAG_NOT_FOCUSABLE
         // and FLAG_ALT_FOCUSABLE_IM flags both set might be set to IME target so they're moved
         // to make room for IME, but the window is not the focused window that's taking input.
-        return (mFocusedApp != null && mFocusedApp.mTask != null) ?
-                mFocusedApp.mTask.mStack : null;
+        return (mFocusedApp != null && mFocusedApp.getTask() != null) ?
+                mFocusedApp.getTask().mStack : null;
     }
 
     public boolean detectSafeMode() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 14f14c5..6d572d7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -844,20 +844,24 @@
         // Make sure the content and visible frames are inside of the
         // final window frame.
         if (windowsAreFloating && !mFrame.isEmpty()) {
+            // For pinned workspace the frame isn't limited in any particular
+            // way since SystemUI controls the bounds. For freeform however
+            // we want to keep things inside the content frame.
+            final Rect limitFrame = task.inPinnedWorkspace() ? mFrame : mContentFrame;
             // Keep the frame out of the blocked system area, limit it in size to the content area
             // and make sure that there is always a minimum visible so that the user can drag it
             // into a usable area..
-            final int height = Math.min(mFrame.height(), mContentFrame.height());
-            final int width = Math.min(mContentFrame.width(), mFrame.width());
+            final int height = Math.min(mFrame.height(), limitFrame.height());
+            final int width = Math.min(limitFrame.width(), mFrame.width());
             final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
             final int minVisibleHeight = Math.min(height, WindowManagerService.dipToPixel(
                     MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics));
             final int minVisibleWidth = Math.min(width, WindowManagerService.dipToPixel(
                     MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics));
-            final int top = Math.max(mContentFrame.top,
-                    Math.min(mFrame.top, mContentFrame.bottom - minVisibleHeight));
-            final int left = Math.max(mContentFrame.left + minVisibleWidth - width,
-                    Math.min(mFrame.left, mContentFrame.right - minVisibleWidth));
+            final int top = Math.max(limitFrame.top,
+                    Math.min(mFrame.top, limitFrame.bottom - minVisibleHeight));
+            final int left = Math.max(limitFrame.left + minVisibleWidth - width,
+                    Math.min(mFrame.left, limitFrame.right - minVisibleWidth));
             mFrame.set(left, top, left + width, top + height);
             mContentFrame.set(mFrame);
             mVisibleFrame.set(mContentFrame);
@@ -1202,7 +1206,7 @@
     }
 
     Task getTask() {
-        return mAppToken != null ? mAppToken.mTask : null;
+        return mAppToken != null ? mAppToken.getTask() : null;
     }
 
     TaskStack getStack() {
@@ -2378,8 +2382,8 @@
 
     /** @return true if this window desires touch events. */
     boolean canReceiveTouchInput() {
-        return mAppToken != null && mAppToken.mTask != null
-                && mAppToken.mTask.mStack.shouldIgnoreInput();
+        return mAppToken != null && mAppToken.getTask() != null
+                && mAppToken.getTask().mStack.shouldIgnoreInput();
     }
 
     @Override
@@ -2712,29 +2716,32 @@
             destroyedSomething |= c.destroySurface(cleanupOnResume, appStopped);
         }
 
-        if (appStopped || mWindowRemovalAllowed || cleanupOnResume) {
-
-            mWinAnimator.destroyPreservedSurfaceLocked();
-
-            if (mDestroying) {
-                if (DEBUG_ADD_REMOVE) Slog.e(TAG_WM, "win=" + this
-                        + " destroySurfaces: appStopped=" + appStopped
-                        + " win.mWindowRemovalAllowed=" + mWindowRemovalAllowed
-                        + " win.mRemoveOnExit=" + mRemoveOnExit);
-
-                if (!cleanupOnResume || mRemoveOnExit) {
-                    destroyOrSaveSurface();
-                }
-                if (mRemoveOnExit) {
-                    removeImmediately();
-                }
-                if (cleanupOnResume) {
-                    requestUpdateWallpaperIfNeeded();
-                }
-                mDestroying = false;
-                destroyedSomething = true;
-            }
+        if (!(appStopped || mWindowRemovalAllowed || cleanupOnResume)) {
+            return destroyedSomething;
         }
+
+        if (appStopped || mWindowRemovalAllowed) {
+            mWinAnimator.destroyPreservedSurfaceLocked();
+        }
+
+        if (mDestroying) {
+            if (DEBUG_ADD_REMOVE) Slog.e(TAG_WM, "win=" + this
+                    + " destroySurfaces: appStopped=" + appStopped
+                    + " win.mWindowRemovalAllowed=" + mWindowRemovalAllowed
+                    + " win.mRemoveOnExit=" + mRemoveOnExit);
+            if (!cleanupOnResume || mRemoveOnExit) {
+                destroyOrSaveSurface();
+            }
+            if (mRemoveOnExit) {
+                removeImmediately();
+            }
+            if (cleanupOnResume) {
+                requestUpdateWallpaperIfNeeded();
+            }
+            mDestroying = false;
+            destroyedSomething = true;
+        }
+
         return destroyedSomething;
     }
 
@@ -4378,7 +4385,8 @@
             // will keep their surface and its size may change over time.
             if (mHasSurface && !isChildWindow()) {
                 mWinAnimator.preserveSurfaceLocked();
-                result |= RELAYOUT_RES_FIRST_TIME;
+                result |= RELAYOUT_RES_SURFACE_CHANGED |
+                    RELAYOUT_RES_FIRST_TIME;
             }
         }
         final boolean freeformResizing = isDragResizing()
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 4b71338..98598e1 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -566,20 +566,6 @@
         if (!mDestroyPreservedSurfaceUponRedraw) {
             return;
         }
-        if (mSurfaceController != null) {
-            if (mPendingDestroySurface != null) {
-                // If we are preserving a surface but we aren't relaunching that means
-                // we are just doing an in-place switch. In that case any SurfaceFlinger side
-                // child layers need to be reparented to the new surface to make this
-                // transparent to the app.
-                if (mWin.mAppToken == null || mWin.mAppToken.isRelaunching() == false) {
-                    SurfaceControl.openTransaction();
-                    mPendingDestroySurface.reparentChildrenInTransaction(mSurfaceController);
-                    SurfaceControl.closeTransaction();
-                }
-            }
-        }
-
         destroyDeferredSurfaceLocked();
         mDestroyPreservedSurfaceUponRedraw = false;
     }
@@ -1979,10 +1965,4 @@
         }
         return mForceScaleUntilResize;
     }
-
-    void detachChildren() {
-        if (mSurfaceController != null) {
-            mSurfaceController.detachChildren();
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index f7d3343..f8e7428 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -135,20 +135,6 @@
         }
     }
 
-    void reparentChildrenInTransaction(WindowSurfaceController other) {
-        if (SHOW_TRANSACTIONS) Slog.i(TAG, "REPARENT from: " + this + " to: " + other);
-        if ((mSurfaceControl != null) && (other.mSurfaceControl != null)) {
-            mSurfaceControl.reparentChildren(other.getHandle());
-        }
-    }
-
-    void detachChildren() {
-        if (SHOW_TRANSACTIONS) Slog.i(TAG, "SEVER CHILDREN");
-        if (mSurfaceControl != null) {
-            mSurfaceControl.detachChildren();
-        }
-    }
-
     void hideInTransaction(String reason) {
         if (SHOW_TRANSACTIONS) logSurface("HIDE ( " + reason + " )", null);
         mHiddenForOtherReasons = true;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 897d5b86..f247ebe 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -651,7 +651,7 @@
         if (openingAppAnimator == null || openingAppAnimator.animation == null) {
             return;
         }
-        final int taskId = appToken.mTask.mTaskId;
+        final int taskId = appToken.getTask().mTaskId;
         Bitmap thumbnailHeader = mService.mAppTransition.getAppTransitionThumbnailHeader(taskId);
         if (thumbnailHeader == null || thumbnailHeader.getConfig() == Bitmap.Config.ALPHA_8) {
             if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "No thumbnail header bitmap for: " + taskId);
diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp
index 74af639..813dcf5 100644
--- a/services/core/jni/com_android_server_lights_LightsService.cpp
+++ b/services/core/jni/com_android_server_lights_LightsService.cpp
@@ -43,28 +43,86 @@
 static bool validate(jint light, jint flash, jint brightness) {
     bool valid = true;
 
-    if (light < 0 || light >= static_cast<int>(Type::COUNT)) {
+    if (light < 0 || light >= static_cast<jint>(Type::COUNT)) {
         ALOGE("Invalid light parameter %d.", light);
         valid = false;
     }
 
-    if (flash != static_cast<int>(Flash::NONE) &&
-        flash != static_cast<int>(Flash::TIMED) &&
-        flash != static_cast<int>(Flash::HARDWARE)) {
+    if (flash != static_cast<jint>(Flash::NONE) &&
+        flash != static_cast<jint>(Flash::TIMED) &&
+        flash != static_cast<jint>(Flash::HARDWARE)) {
         ALOGE("Invalid flash parameter %d.", flash);
         valid = false;
     }
 
-    if (brightness != static_cast<int>(Brightness::USER) &&
-        brightness != static_cast<int>(Brightness::SENSOR) &&
-        brightness != static_cast<int>(Brightness::LOW_PERSISTENCE)) {
+    if (brightness != static_cast<jint>(Brightness::USER) &&
+        brightness != static_cast<jint>(Brightness::SENSOR) &&
+        brightness != static_cast<jint>(Brightness::LOW_PERSISTENCE)) {
         ALOGE("Invalid brightness parameter %d.", brightness);
         valid = false;
     }
 
+    if (brightness == static_cast<jint>(Brightness::LOW_PERSISTENCE) &&
+        light != static_cast<jint>(Type::BACKLIGHT)) {
+        ALOGE("Cannot set low-persistence mode for non-backlight device.");
+        valid = false;
+    }
+
     return valid;
 }
 
+static LightState constructState(
+        jint colorARGB,
+        jint flashMode,
+        jint onMS,
+        jint offMS,
+        jint brightnessMode){
+    Flash flash = static_cast<Flash>(flashMode);
+    Brightness brightness = static_cast<Brightness>(brightnessMode);
+
+    LightState state{};
+
+    if (brightness == Brightness::LOW_PERSISTENCE) {
+        state.flashMode = Flash::NONE;
+    } else {
+        // Only set non-brightness settings when not in low-persistence mode
+        state.flashMode = flash;
+        state.flashOnMs = onMS;
+        state.flashOffMs = offMS;
+    }
+
+    state.color = colorARGB;
+    state.brightnessMode = brightness;
+
+    return state;
+}
+
+static void processReturn(
+        const Return<Status> &ret,
+        Type type,
+        const LightState &state) {
+    if (!ret.isOk()) {
+        ALOGE("Failed to issue set light command.");
+        gLight = nullptr;
+        return;
+    }
+
+    switch (static_cast<Status>(ret)) {
+        case Status::SUCCESS:
+            break;
+        case Status::LIGHT_NOT_SUPPORTED:
+            ALOGE("Light requested not available on this device. %d", type);
+            break;
+        case Status::BRIGHTNESS_NOT_SUPPORTED:
+            ALOGE("Brightness parameter not supported on this device: %d",
+                state.brightnessMode);
+            break;
+        case Status::UNKNOWN:
+        default:
+            ALOGE("Unknown error setting light.");
+    }
+}
+
 static void setLight_native(
         JNIEnv* /* env */,
         jobject /* clazz */,
@@ -79,63 +137,23 @@
         return;
     }
 
-    // TODO(b/31632518)
-    if (gLight == nullptr) {
+    if (gLight == nullptr || !gLight->ping().isOk()) {
         gLight = ILight::getService();
     }
 
     if (gLight == nullptr) {
-        ALOGE("LightService unable to get ILight interface.");
+        ALOGE("Unable to get ILight interface.");
         return;
     }
 
     Type type = static_cast<Type>(light);
-    Flash flash = static_cast<Flash>(flashMode);
-    Brightness brightness = static_cast<Brightness>(brightnessMode);
+    LightState state = constructState(
+        colorARGB, flashMode, onMS, offMS, brightnessMode);
 
-    LightState state{};
-
-    if (brightnessMode == static_cast<int>(Brightness::LOW_PERSISTENCE)) {
-        if (light != static_cast<int>(Type::BACKLIGHT)) {
-            ALOGE("Cannot set low-persistence mode for non-backlight device.");
-            return;
-        }
-        state.flashMode = Flash::NONE;
-    } else {
-        // Only set non-brightness settings when not in low-persistence mode
-        state.flashMode = flash;
-        state.flashOnMs = onMS;
-        state.flashOffMs = offMS;
-    }
-
-    state.color = colorARGB;
-    state.brightnessMode = brightness;
-
-    Status status;
     {
         ALOGD_IF_SLOW(50, "Excessive delay setting light");
         Return<Status> ret = gLight->setLight(type, state);
-
-        if (!ret.isOk()) {
-            ALOGE("Failed to issue set light command.");
-            return;
-        }
-
-        status = static_cast<Status>(ret); // hal status
-    }
-
-    switch (status) {
-        case Status::SUCCESS:
-            break;
-        case Status::LIGHT_NOT_SUPPORTED:
-            ALOGE("Light requested not availale on this device.");
-            break;
-        case Status::BRIGHTNESS_NOT_SUPPORTED:
-            ALOGE("Brightness parameter not supported on this device.");
-            break;
-        case Status::UNKNOWN:
-        default:
-            ALOGE("Unknown error setting light.");
+        processReturn(ret, type, state);
     }
 }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8b94ca0..9c3ecd0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -99,6 +99,7 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.StringParceledListSlice;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -166,6 +167,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
@@ -243,10 +245,14 @@
 
     private static final String TAG_DEFAULT_INPUT_METHOD_SET = "default-ime-set";
 
+    private static final String TAG_OWNER_INSTALLED_CA_CERT = "owner-installed-ca-cert";
+
     private static final String ATTR_ID = "id";
 
     private static final String ATTR_VALUE = "value";
 
+    private static final String ATTR_ALIAS = "alias";
+
     private static final String TAG_INITIALIZATION_BUNDLE = "initialization-bundle";
 
     private static final String TAG_PASSWORD_TOKEN_HANDLE = "password-token";
@@ -480,6 +486,7 @@
         final ArrayList<ActiveAdmin> mAdminList = new ArrayList<>();
         final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
 
+        // TODO(b/35385311): Keep track of metadata in TrustedCertificateStore instead.
         final ArraySet<String> mAcceptedCaCertificates = new ArraySet<>();
 
         // This is the list of component allowed to start lock task mode.
@@ -504,6 +511,9 @@
 
         boolean mDefaultInputMethodSet = false;
 
+        // TODO(b/35385311): Keep track of metadata in TrustedCertificateStore instead.
+        Set<String> mOwnerInstalledCaCerts = new ArraySet<>();
+
         // Used for initialization of users created by createAndManageUsers.
         boolean mAdminBroadcastPending = false;
         PersistableBundle mInitBundle = null;
@@ -518,6 +528,7 @@
     final SparseArray<DevicePolicyData> mUserData = new SparseArray<>();
 
     final Handler mHandler;
+    final Handler mBackgroundHandler;
 
     /** Listens on any device, even when mHasFeature == false. */
     final BroadcastReceiver mRootCaReceiver = new BroadcastReceiver() {
@@ -619,6 +630,25 @@
                 handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
             } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)) {
                 clearWipeProfileNotification();
+            } else if (KeyChain.ACTION_TRUST_STORE_CHANGED.equals(intent.getAction())) {
+                mBackgroundHandler.post(() ->  {
+                    try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(
+                            UserHandle.of(userHandle))) {
+                        final List<String> caCerts =
+                                keyChainConnection.getService().getUserCaAliases().getList();
+                        synchronized (DevicePolicyManagerService.this) {
+                            if (getUserData(userHandle).mOwnerInstalledCaCerts
+                                    .retainAll(caCerts)) {
+                                saveSettingsLocked(userHandle);
+                            }
+                        }
+                    } catch (InterruptedException e) {
+                        Slog.w(LOG_TAG, "error talking to IKeyChainService", e);
+                        Thread.currentThread().interrupt();
+                    } catch (RemoteException e) {
+                        Slog.w(LOG_TAG, "error talking to IKeyChainService", e);
+                    }
+                });
             }
         }
 
@@ -1786,6 +1816,7 @@
                 .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
         mIsWatch = mInjector.getPackageManager()
                 .hasSystemFeature(PackageManager.FEATURE_WATCH);
+        mBackgroundHandler = BackgroundThread.getHandler();
 
         // Broadcast filter for changes to the trusted certificate store. These changes get a
         // separate intent filter so we can listen to them even when device_admin is off.
@@ -1807,6 +1838,7 @@
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_STARTED);
+        filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
         filter = new IntentFilter();
@@ -2580,6 +2612,12 @@
                 out.endTag(null, TAG_DEFAULT_INPUT_METHOD_SET);
             }
 
+            for (final String cert : policy.mOwnerInstalledCaCerts) {
+                out.startTag(null, TAG_OWNER_INSTALLED_CA_CERT);
+                out.attribute(null, ATTR_ALIAS, cert);
+                out.endTag(null, TAG_OWNER_INSTALLED_CA_CERT);
+            }
+
             out.endTag(null, "policies");
 
             out.endDocument();
@@ -2696,6 +2734,7 @@
             policy.mAdminList.clear();
             policy.mAdminMap.clear();
             policy.mAffiliationIds.clear();
+            policy.mOwnerInstalledCaCerts.clear();
             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -2791,6 +2830,8 @@
                             parser.getAttributeValue(null, ATTR_VALUE));
                 } else if (TAG_DEFAULT_INPUT_METHOD_SET.equals(tag)) {
                     policy.mDefaultInputMethodSet = true;
+                } else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) {
+                    policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS));
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -4691,17 +4732,15 @@
             return false;
         }
 
-        final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+        final UserHandle userHandle = UserHandle.of(mInjector.userHandleGetCallingUserId());
         final long id = mInjector.binderClearCallingIdentity();
+        String alias = null;
         try {
-            final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
-            try {
-                keyChainConnection.getService().installCaCertificate(pemCert);
-                return true;
+            try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(
+                    userHandle)) {
+                alias = keyChainConnection.getService().installCaCertificate(pemCert);
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
-            } finally {
-                keyChainConnection.close();
             }
         } catch (InterruptedException e1) {
             Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
@@ -4709,6 +4748,16 @@
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
+        if (alias == null) {
+            Log.w(LOG_TAG, "Problem installing cert");
+        } else {
+            synchronized (this) {
+                final int userId = userHandle.getIdentifier();
+                getUserData(userId).mOwnerInstalledCaCerts.add(alias);
+                saveSettingsLocked(userId);
+            }
+            return true;
+        }
         return false;
     }
 
@@ -4722,25 +4771,31 @@
     public void uninstallCaCerts(ComponentName admin, String callerPackage, String[] aliases) {
         enforceCanManageCaCerts(admin, callerPackage);
 
-        final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+        final int userId = mInjector.userHandleGetCallingUserId();
+        final UserHandle userHandle = UserHandle.of(userId);
         final long id = mInjector.binderClearCallingIdentity();
         try {
-            final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle);
-            try {
+            try (final KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(
+                    userHandle)) {
                 for (int i = 0 ; i < aliases.length; i++) {
                     keyChainConnection.getService().deleteCaCertificate(aliases[i]);
                 }
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, "from CaCertUninstaller: ", e);
-            } finally {
-                keyChainConnection.close();
+                return;
             }
         } catch (InterruptedException ie) {
             Log.w(LOG_TAG, "CaCertUninstaller: ", ie);
             Thread.currentThread().interrupt();
+            return;
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
+        synchronized (this) {
+            if (getUserData(userId).mOwnerInstalledCaCerts.removeAll(Arrays.asList(aliases))) {
+                saveSettingsLocked(userId);
+            }
+        }
     }
 
     @Override
@@ -6731,6 +6786,7 @@
         }
         final DevicePolicyData policyData = getUserData(userId);
         policyData.mDefaultInputMethodSet = false;
+        policyData.mOwnerInstalledCaCerts.clear();
         saveSettingsLocked(userId);
         clearUserPoliciesLocked(userId);
         mOwners.removeProfileOwner(userId);
@@ -7127,29 +7183,32 @@
     }
 
     private void enforceFullCrossUsersPermission(int userHandle) {
-        enforceSystemUserOrPermission(userHandle,
+        enforceSystemUserOrPermissionIfCrossUser(userHandle,
                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
     }
 
     private void enforceCrossUsersPermission(int userHandle) {
-        enforceSystemUserOrPermission(userHandle,
+        enforceSystemUserOrPermissionIfCrossUser(userHandle,
                 android.Manifest.permission.INTERACT_ACROSS_USERS);
     }
 
-    private void enforceSystemUserOrPermission(int userHandle, String permission) {
-        if (userHandle < 0) {
-            throw new IllegalArgumentException("Invalid userId " + userHandle);
-        }
-        final int callingUid = mInjector.binderGetCallingUid();
-        if (userHandle == UserHandle.getUserId(callingUid)) {
-            return;
-        }
-        if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) {
+    private void enforceSystemUserOrPermission(String permission) {
+        if (!(isCallerWithSystemUid() || mInjector.binderGetCallingUid() == Process.ROOT_UID)) {
             mContext.enforceCallingOrSelfPermission(permission,
                     "Must be system or have " + permission + " permission");
         }
     }
 
+    private void enforceSystemUserOrPermissionIfCrossUser(int userHandle, String permission) {
+        if (userHandle < 0) {
+            throw new IllegalArgumentException("Invalid userId " + userHandle);
+        }
+        if (userHandle == mInjector.userHandleGetCallingUserId()) {
+            return;
+        }
+        enforceSystemUserOrPermission(permission);
+    }
+
     private void enforceManagedProfile(int userHandle, String message) {
         if(!isManagedProfile(userHandle)) {
             throw new SecurityException("You can not " + message + " outside a managed profile.");
@@ -7184,6 +7243,21 @@
                 "Only profile owner, device owner and system may call this method.");
     }
 
+    private void enforceProfileOwnerOrFullCrossUsersPermission(int userId) {
+        if (userId == mInjector.userHandleGetCallingUserId()) {
+            synchronized (this) {
+                if (getActiveAdminWithPolicyForUidLocked(null,
+                        DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid())
+                                != null) {
+                    // Device Owner/Profile Owner may access the user it runs on.
+                    return;
+                }
+            }
+        }
+        // Otherwise, INTERACT_ACROSS_USERS_FULL permission, system UID or root UID is required.
+        enforceSystemUserOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    }
+
     private void ensureCallerPackage(@Nullable String packageName) {
         if (packageName == null) {
             Preconditions.checkState(isCallerWithSystemUid(),
@@ -10897,4 +10971,14 @@
         }
         return getUserData(userId).mDefaultInputMethodSet;
     }
+
+    @Override
+    public StringParceledListSlice getOwnerInstalledCaCerts(@NonNull UserHandle user) {
+        final int userId = user.getIdentifier();
+        enforceProfileOwnerOrFullCrossUsersPermission(userId);
+        synchronized (this) {
+            return new StringParceledListSlice(
+                    new ArrayList<>(getUserData(userId).mOwnerInstalledCaCerts));
+        }
+    }
 }
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 6212626..25c29ee 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -196,6 +196,7 @@
     private void compareChannels(NotificationChannel expected, NotificationChannel actual) {
         assertEquals(expected.getId(), actual.getId());
         assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getNameResId(), actual.getNameResId());
         assertEquals(expected.shouldVibrate(), actual.shouldVibrate());
         assertEquals(expected.shouldShowLights(), actual.shouldShowLights());
         assertEquals(expected.getImportance(), actual.getImportance());
@@ -208,6 +209,12 @@
         assertEquals(expected.getLightColor(), actual.getLightColor());
     }
 
+    private void compareGroups(NotificationChannelGroup expected, NotificationChannelGroup actual) {
+        assertEquals(expected.getId(), actual.getId());
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getNameResId(), actual.getNameResId());
+    }
+
     @Test
     public void testFindAfterRankingWithASplitGroup() throws Exception {
         ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3);
@@ -260,11 +267,15 @@
 
     @Test
     public void testChannelXml() throws Exception {
-        NotificationChannelGroup ncg = new NotificationChannelGroup("1", "2");
+        int nameResId = 924896;
+        int groupNameResId = 426272;
+
+        NotificationChannelGroup ncg = new NotificationChannelGroup("1", groupNameResId);
+        NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         NotificationChannel channel2 =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+                new NotificationChannel("id2", nameResId, IMPORTANCE_LOW);
         channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
         channel2.enableLights(true);
         channel2.setBypassDnd(true);
@@ -275,6 +286,7 @@
         channel2.setLightColor(Color.BLUE);
 
         mHelper.createNotificationChannelGroup(pkg, uid, ncg, true);
+        mHelper.createNotificationChannelGroup(pkg, uid, ncg2, true);
         mHelper.createNotificationChannel(pkg, uid, channel1, true);
         mHelper.createNotificationChannel(pkg, uid, channel2, false);
 
@@ -305,7 +317,9 @@
         for (NotificationChannelGroup actual : actualGroups) {
             if (ncg.getId().equals(actual.getId())) {
                 foundNcg = true;
-                 break;
+                compareGroups(ncg, actual);
+            } else if (ncg2.getId().equals(actual.getId())) {
+                compareGroups(ncg2, actual);
             }
         }
         assertTrue(foundNcg);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 756514b..f797f31 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -49,6 +49,8 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
+import android.security.IKeyChainService;
+import android.security.KeyChain;
 import android.telephony.TelephonyManager;
 import android.test.MoreAsserts;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -122,6 +124,34 @@
     public DevicePolicyManager dpm;
     public DevicePolicyManagerServiceTestable dpms;
 
+    /*
+     * The CA cert below is the content of cacert.pem as generated by:
+     *
+     * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+     */
+    private static final String TEST_CA =
+            "-----BEGIN CERTIFICATE-----\n" +
+            "MIIDXTCCAkWgAwIBAgIJAK9Tl/F9V8kSMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n" +
+            "BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n" +
+            "aWRnaXRzIFB0eSBMdGQwHhcNMTUwMzA2MTczMjExWhcNMjUwMzAzMTczMjExWjBF\n" +
+            "MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n" +
+            "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" +
+            "CgKCAQEAvItOutsE75WBTgTyNAHt4JXQ3JoseaGqcC3WQij6vhrleWi5KJ0jh1/M\n" +
+            "Rpry7Fajtwwb4t8VZa0NuM2h2YALv52w1xivql88zce/HU1y7XzbXhxis9o6SCI+\n" +
+            "oVQSbPeXRgBPppFzBEh3ZqYTVhAqw451XhwdA4Aqs3wts7ddjwlUzyMdU44osCUg\n" +
+            "kVg7lfPf9sTm5IoHVcfLSCWH5n6Nr9sH3o2ksyTwxuOAvsN11F/a0mmUoPciYPp+\n" +
+            "q7DzQzdi7akRG601DZ4YVOwo6UITGvDyuAAdxl5isovUXqe6Jmz2/myTSpAKxGFs\n" +
+            "jk9oRoG6WXWB1kni490GIPjJ1OceyQIDAQABo1AwTjAdBgNVHQ4EFgQUH1QIlPKL\n" +
+            "p2OQ/AoLOjKvBW4zK3AwHwYDVR0jBBgwFoAUH1QIlPKLp2OQ/AoLOjKvBW4zK3Aw\n" +
+            "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAcMi4voMMJHeQLjtq8Oky\n" +
+            "Azpyk8moDwgCd4llcGj7izOkIIFqq/lyqKdtykVKUWz2bSHO5cLrtaOCiBWVlaCV\n" +
+            "DYAnnVLM8aqaA6hJDIfaGs4zmwz0dY8hVMFCuCBiLWuPfiYtbEmjHGSmpQTG6Qxn\n" +
+            "ZJlaK5CZyt5pgh5EdNdvQmDEbKGmu0wpCq9qjZImwdyAul1t/B0DrsWApZMgZpeI\n" +
+            "d2od0VBrCICB1K4p+C51D93xyQiva7xQcCne+TAnGNy9+gjQ/MyR8MRpwRLv5ikD\n" +
+            "u0anJCN8pXo6IMglfMAsoton1J6o5/ae5uhC6caQU8bNUsCK570gpNfjkzo6rbP0\n" +
+            "wQ==\n" +
+            "-----END CERTIFICATE-----\n";
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -3916,6 +3946,124 @@
         assertFalse(dpm.isDefaultInputMethodSetByOwner(secondUser));
     }
 
+    public void testGetOwnerInstalledCaCertsForDeviceOwner() throws Exception {
+        setDeviceOwner();
+
+        mContext.packageName = admin1.getPackageName();
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        verifyCanGetOwnerInstalledCaCerts(admin1);
+    }
+
+    public void testGetOwnerInstalledCaCertsForProfileOwner() throws Exception {
+        setAsProfileOwner(admin1);
+
+        mContext.packageName = admin1.getPackageName();
+        verifyCanGetOwnerInstalledCaCerts(admin1);
+        verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(admin1);
+    }
+
+    public void testGetOwnerInstalledCaCertsForDelegate() throws Exception {
+        setAsProfileOwner(admin1);
+
+        final String delegate = "com.example.delegate";
+        final int delegateUid = setupPackageInPackageManager(delegate, 20988);
+        dpm.setCertInstallerPackage(admin1, delegate);
+
+        mContext.packageName = delegate;
+        mContext.binder.callingUid = delegateUid;
+        verifyCanGetOwnerInstalledCaCerts(null);
+        verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(null);
+    }
+
+    private void verifyCanGetOwnerInstalledCaCerts(ComponentName caller) throws Exception {
+        final UserHandle user = UserHandle.getUserHandleForUid(mContext.binder.callingUid);
+        final int ownerUid = user.equals(UserHandle.SYSTEM) ?
+                DpmMockContext.CALLER_SYSTEM_USER_UID : DpmMockContext.CALLER_UID;
+
+        mContext.applicationInfo = new ApplicationInfo();
+        mContext.userContexts.put(user, mContext);
+        when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+
+        // Install a CA cert.
+        final String alias = "cert";
+        final byte[] caCert = TEST_CA.getBytes();
+        when(mContext.keyChainConnection.getService().installCaCertificate(caCert))
+                .thenReturn(alias);
+        assertTrue(dpm.installCaCert(caller, caCert));
+        when(mContext.keyChainConnection.getService().getUserCaAliases())
+                .thenReturn(asSlice(new String[] {alias}));
+        mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED));
+        flushTasks();
+
+        // Device Owner / Profile Owner can find out which CA certs were installed by itself.
+        final String packageName = mContext.packageName;
+        mContext.packageName = admin1.getPackageName();
+        final long callerIdentity = mContext.binder.clearCallingIdentity();
+        mContext.binder.callingUid = ownerUid;
+        List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user);
+        assertNotNull(ownerInstalledCaCerts);
+        assertEquals(1, ownerInstalledCaCerts.size());
+        assertTrue(ownerInstalledCaCerts.contains(alias));
+
+        // Restarting the DPMS should not lose information.
+        initializeDpms();
+        assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(user));
+
+        // System can find out which CA certs were installed by the Device Owner / Profile Owner.
+        mContext.packageName = "com.android.frameworks.servicestests";
+        mContext.binder.clearCallingIdentity();
+        assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(user));
+
+        // Remove the CA cert.
+        mContext.packageName = packageName;
+        mContext.binder.restoreCallingIdentity(callerIdentity);
+        reset(mContext.keyChainConnection.getService());
+        mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED));
+        flushTasks();
+
+        // Verify that the CA cert is no longer reported as installed by the Device Owner / Profile
+        // Owner.
+        mContext.packageName = admin1.getPackageName();
+        mContext.binder.callingUid = ownerUid;
+        ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user);
+        assertNotNull(ownerInstalledCaCerts);
+        assertTrue(ownerInstalledCaCerts.isEmpty());
+
+        mContext.packageName = packageName;
+        mContext.binder.restoreCallingIdentity(callerIdentity);
+    }
+
+    private void verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(ComponentName caller)
+            throws Exception {
+        final UserHandle user = UserHandle.of(DpmMockContext.CALLER_USER_HANDLE);
+
+        mContext.applicationInfo = new ApplicationInfo();
+        mContext.userContexts.put(user, mContext);
+        when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+
+        // Install a CA cert.
+        final String alias = "cert";
+        final byte[] caCert = TEST_CA.getBytes();
+        when(mContext.keyChainConnection.getService().installCaCertificate(caCert))
+                .thenReturn(alias);
+        assertTrue(dpm.installCaCert(caller, caCert));
+        when(mContext.keyChainConnection.getService().getUserCaAliases())
+                .thenReturn(asSlice(new String[] {alias}));
+        mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED));
+        flushTasks();
+
+        // Removing the Profile Owner should clear the information which CA certs were installed
+        // by it.
+        mContext.packageName = admin1.getPackageName();
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        dpm.clearProfileOwner(admin1);
+        mContext.packageName = "com.android.frameworks.servicestests";
+        mContext.binder.clearCallingIdentity();
+        final List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user);
+        assertNotNull(ownerInstalledCaCerts);
+        assertTrue(ownerInstalledCaCerts.isEmpty());
+    }
+
     private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {
         when(mContext.settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
                 userhandle)).thenReturn(isUserSetupComplete ? 1 : 0);
@@ -3978,4 +4126,31 @@
     private static StringParceledListSlice asSlice(String[] s) {
         return new StringParceledListSlice(Arrays.asList(s));
     }
+
+    private void flushTasks() throws Exception {
+        Boolean tasksFlushed[] = new Boolean[] {false};
+        final Runnable tasksFlushedNotifier = () -> {
+            synchronized (tasksFlushed) {
+                tasksFlushed[0] = true;
+                tasksFlushed.notify();
+            }
+        };
+
+        // Flush main thread handler.
+        dpms.mHandler.post(tasksFlushedNotifier);
+        synchronized (tasksFlushed) {
+            if (!tasksFlushed[0]) {
+                tasksFlushed.wait();
+            }
+        }
+
+        // Flush background thread handler.
+        tasksFlushed[0] = false;
+        dpms.mBackgroundHandler.post(tasksFlushedNotifier);
+        synchronized (tasksFlushed) {
+            if (!tasksFlushed[0]) {
+                tasksFlushed.wait();
+            }
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 258b393..7d017c5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -316,6 +316,41 @@
 
     public ApplicationInfo applicationInfo = null;
 
+    // We have to keep track of broadcast receivers registered for a given intent ourselves as the
+    // DPM unit tests mock out the package manager and PackageManager.queryBroadcastReceivers() does
+    // not work.
+    private class BroadcastReceiverRegistration {
+        public final BroadcastReceiver receiver;
+        public final IntentFilter filter;
+        public final Handler scheduler;
+
+        public BroadcastReceiverRegistration(BroadcastReceiver receiver, IntentFilter filter,
+                Handler scheduler) {
+            this.receiver = receiver;
+            this.filter = filter;
+            this.scheduler = scheduler;
+        }
+
+        public void sendBroadcastIfApplicable(int userId, Intent intent) {
+            final BroadcastReceiver.PendingResult result = new BroadcastReceiver.PendingResult(
+                    0 /* resultCode */, null /* resultData */, null /* resultExtras */,
+                    0 /* type */, false /* ordered */, false /* sticky */, null /* token */, userId,
+                    0 /* flags */);
+            if (filter.match(null, intent, false, "DpmMockContext") > 0) {
+                if (scheduler != null) {
+                    scheduler.post(() -> {
+                        receiver.setPendingResult(result);
+                        receiver.onReceive(DpmMockContext.this, intent);
+                    });
+                } else {
+                    receiver.setPendingResult(result);
+                    receiver.onReceive(DpmMockContext.this, intent);
+                }
+            }
+        }
+    }
+    private List<BroadcastReceiverRegistration> mBroadcastReceivers = new ArrayList<>();
+
     public DpmMockContext(Context context, File dataDir) {
         realTestContext = context;
 
@@ -476,6 +511,13 @@
                 .thenReturn(isRunning);
     }
 
+    public void injectBroadcast(Intent intent) {
+        final int userId = UserHandle.getUserId(binder.getCallingUid());
+        for (final BroadcastReceiverRegistration receiver : mBroadcastReceivers) {
+            receiver.sendBroadcastIfApplicable(userId, intent);
+        }
+    }
+
     @Override
     public Resources getResources() {
         return resources;
@@ -681,24 +723,28 @@
 
     @Override
     public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        mBroadcastReceivers.add(new BroadcastReceiverRegistration(receiver, filter, null));
         return spiedContext.registerReceiver(receiver, filter);
     }
 
     @Override
     public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
             String broadcastPermission, Handler scheduler) {
+        mBroadcastReceivers.add(new BroadcastReceiverRegistration(receiver, filter, scheduler));
         return spiedContext.registerReceiver(receiver, filter, broadcastPermission, scheduler);
     }
 
     @Override
     public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
             IntentFilter filter, String broadcastPermission, Handler scheduler) {
+        mBroadcastReceivers.add(new BroadcastReceiverRegistration(receiver, filter, scheduler));
         return spiedContext.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
                 scheduler);
     }
 
     @Override
     public void unregisterReceiver(BroadcastReceiver receiver) {
+        mBroadcastReceivers.removeIf(r -> r.receiver == receiver);
         spiedContext.unregisterReceiver(receiver);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
index 6c6eb7e..d665094 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
@@ -69,7 +69,7 @@
     class TestParallelPackageParser extends ParallelPackageParser {
 
         TestParallelPackageParser() {
-            super(null, false, null, null);
+            super(null, false, null, null, null);
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 94ff07f..4b3c2f8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -247,7 +247,7 @@
         final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1);
         final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
                 getTestContext().getResources(), R.drawable.icon2));
-        final Icon icon3 = Icon.createWithMaskableBitmap(BitmapFactory.decodeResource(
+        final Icon icon3 = Icon.createWithAdaptiveBitmap(BitmapFactory.decodeResource(
             getTestContext().getResources(), R.drawable.icon2));
 
         final ShortcutInfo si1 = makeShortcut(
@@ -567,7 +567,7 @@
 
         final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
                 getTestContext().getResources(), R.drawable.black_32x32));
-        final Icon bmp64x64_maskable = Icon.createWithMaskableBitmap(BitmapFactory.decodeResource(
+        final Icon bmp64x64_maskable = Icon.createWithAdaptiveBitmap(BitmapFactory.decodeResource(
                 getTestContext().getResources(), R.drawable.black_64x64));
         final Icon bmp512x512 = Icon.createWithBitmap(BitmapFactory.decodeResource(
                 getTestContext().getResources(), R.drawable.black_512x512));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index c54fa02..900da09 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -46,7 +46,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.frameworks.servicestests.R;
-import com.android.server.pm.ShortcutService.ConfigConstants;
 import com.android.server.pm.ShortcutUser.PackageWithUser;
 
 import java.io.File;
@@ -993,7 +992,7 @@
 
         setCaller(CALLING_PACKAGE_1, USER_10);
 
-        final Icon bmp32x32 = Icon.createWithMaskableBitmap(BitmapFactory.decodeResource(
+        final Icon bmp32x32 = Icon.createWithAdaptiveBitmap(BitmapFactory.decodeResource(
             getTestContext().getResources(), R.drawable.black_32x32));
 
         PersistableBundle pb = new PersistableBundle();
@@ -1047,7 +1046,7 @@
         assertEquals(1, si.getExtras().getInt("k"));
 
         assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE
-            | ShortcutInfo.FLAG_STRINGS_RESOLVED | ShortcutInfo.FLAG_MASKABLE_BITMAP,
+            | ShortcutInfo.FLAG_STRINGS_RESOLVED | ShortcutInfo.FLAG_ADAPTIVE_BITMAP,
             si.getFlags());
         assertNotNull(si.getBitmapPath()); // Something should be set.
         assertEquals(0, si.getIconResourceId());
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 90a2ec0..fa0bd39 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -16,9 +16,10 @@
 
 package com.android.server.pm.dex;
 
-import android.os.Build;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.os.Build;
+import android.os.UserHandle;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -57,6 +58,7 @@
 
     private int mUser0;
     private int mUser1;
+
     @Before
     public void setup() {
 
@@ -243,6 +245,113 @@
         assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
     }
 
+    @Test
+    public void testNotifyPackageUpdated() {
+        // Foo loads Bar main apks.
+        notifyDexLoad(mFooUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0);
+
+        // Bar is used by others now and should be in our records.
+        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
+        assertNotNull(pui);
+        assertTrue(pui.isUsedByOtherApps());
+        assertTrue(pui.getDexUseInfoMap().isEmpty());
+
+        // Notify that bar is updated.
+        mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
+                mBarUser0.mPackageInfo.applicationInfo.sourceDir,
+                mBarUser0.mPackageInfo.applicationInfo.splitSourceDirs);
+
+        // The usedByOtherApps flag should be clear now.
+        pui = getPackageUseInfo(mBarUser0);
+        assertNotNull(pui);
+        assertFalse(pui.isUsedByOtherApps());
+    }
+
+    @Test
+    public void testNotifyPackageUpdatedCodeLocations() {
+        // Simulate a split update.
+        String newSplit = mBarUser0.replaceLastSplit();
+        List<String> newSplits = new ArrayList<>();
+        newSplits.add(newSplit);
+
+        // We shouldn't find yet the new split as we didn't notify the package update.
+        notifyDexLoad(mFooUser0, newSplits, mUser0);
+        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
+        assertNull(pui);
+
+        // Notify that bar is updated. splitSourceDirs will contain the updated path.
+        mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
+                mBarUser0.mPackageInfo.applicationInfo.sourceDir,
+                mBarUser0.mPackageInfo.applicationInfo.splitSourceDirs);
+
+        // Now, when the split is loaded we will find it and we should mark Bar as usedByOthers.
+        notifyDexLoad(mFooUser0, newSplits, mUser0);
+        pui = getPackageUseInfo(mBarUser0);
+        assertNotNull(pui);
+        assertTrue(pui.isUsedByOtherApps());
+    }
+
+    @Test
+    public void testNotifyPackageDataDestroyForOne() {
+        // Bar loads its own secondary files.
+        notifyDexLoad(mBarUser0, mBarUser0.getSecondaryDexPaths(), mUser0);
+        notifyDexLoad(mBarUser1, mBarUser1.getSecondaryDexPaths(), mUser1);
+
+        mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), mUser0);
+
+        // Bar should not be around since it was removed for all users.
+        PackageUseInfo pui = getPackageUseInfo(mBarUser1);
+        assertNotNull(pui);
+        assertSecondaryUse(mBarUser1, pui, mBarUser1.getSecondaryDexPaths(),
+                /*isUsedByOtherApps*/false, mUser1);
+    }
+
+    @Test
+    public void testNotifyPackageDataDestroyForeignUse() {
+        // Foo loads its own secondary files.
+        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
+        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
+
+        // Bar loads Foo main apks.
+        notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
+
+        mDexManager.notifyPackageDataDestroyed(mFooUser0.getPackageName(), mUser0);
+
+        // Foo should still be around since it's used by other apps but with no
+        // secondary dex info.
+        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+        assertNotNull(pui);
+        assertTrue(pui.isUsedByOtherApps());
+        assertTrue(pui.getDexUseInfoMap().isEmpty());
+    }
+
+    @Test
+    public void testNotifyPackageDataDestroyComplete() {
+        // Foo loads its own secondary files.
+        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
+        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
+
+        mDexManager.notifyPackageDataDestroyed(mFooUser0.getPackageName(), mUser0);
+
+        // Foo should not be around since all its secondary dex info were deleted
+        // and it is not used by other apps.
+        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+        assertNull(pui);
+    }
+
+    @Test
+    public void testNotifyPackageDataDestroyForAll() {
+        // Foo loads its own secondary files.
+        notifyDexLoad(mBarUser0, mBarUser0.getSecondaryDexPaths(), mUser0);
+        notifyDexLoad(mBarUser1, mBarUser1.getSecondaryDexPaths(), mUser1);
+
+        mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), UserHandle.USER_ALL);
+
+        // Bar should not be around since it was removed for all users.
+        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
+        assertNull(pui);
+    }
+
     private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
             List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
         for (String dex : secondaries) {
@@ -317,5 +426,12 @@
             }
             return paths;
         }
+
+        String replaceLastSplit() {
+            int length = mPackageInfo.applicationInfo.splitSourceDirs.length;
+            // Add an extra bogus dex extension to simulate a new split name.
+            mPackageInfo.applicationInfo.splitSourceDirs[length - 1] += ".dex";
+            return mPackageInfo.applicationInfo.splitSourceDirs[length - 1];
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 19e0bcf..2e99433 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -257,6 +257,30 @@
     }
 
     @Test
+    public void testRemovePackage() {
+        // Record Bar secondaries for two different users.
+        assertTrue(record(mBarSecondary1User0));
+        assertTrue(record(mBarSecondary2User1));
+
+        // Remove the package.
+        assertTrue(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName));
+        // Assert that we can't find the package anymore.
+        assertNull(mPackageDexUsage.getPackageUseInfo(mBarSecondary1User0.mPackageName));
+    }
+
+    @Test
+    public void testRemoveNonexistentPackage() {
+        // Record Bar secondaries for two different users.
+        assertTrue(record(mBarSecondary1User0));
+
+        // Remove the package.
+        assertTrue(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName));
+        // Remove the package again. It should return false because the package no longer
+        // has a record in the use info.
+        assertFalse(mPackageDexUsage.removePackage(mBarSecondary1User0.mPackageName));
+    }
+
+    @Test
     public void testRemoveUserPackage() {
         // Record Bar secondaries for two different users.
         assertTrue(record(mBarSecondary1User0));
@@ -282,6 +306,32 @@
         assertPackageDexUsage(null, mBarSecondary2User1);
     }
 
+    @Test
+    public void testClearUsedByOtherApps() {
+        // Write a package which is used by other apps.
+        assertTrue(record(mFooSplit2UsedByOtherApps0));
+        assertTrue(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
+
+        // Check that the package is no longer used by other apps.
+        TestData noLongerUsedByOtherApps = new TestData(
+            mFooSplit2UsedByOtherApps0.mPackageName,
+            mFooSplit2UsedByOtherApps0.mDexFile,
+            mFooSplit2UsedByOtherApps0.mOwnerUserId,
+            mFooSplit2UsedByOtherApps0.mLoaderIsa,
+            /*mIsUsedByOtherApps*/false,
+            mFooSplit2UsedByOtherApps0.mPrimaryOrSplit);
+        assertPackageDexUsage(noLongerUsedByOtherApps);
+    }
+
+    @Test
+    public void testClearUsedByOtherAppsNonexistent() {
+        // Write a package which is used by other apps.
+        assertTrue(record(mFooSplit2UsedByOtherApps0));
+        assertTrue(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
+        // Clearing again should return false as there should be no update on the use info.
+        assertFalse(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
+    }
+
     private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
         String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
         boolean primaryUsedByOtherApps = primary == null ? false : primary.mUsedByOtherApps;
diff --git a/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
new file mode 100644
index 0000000..1d62e01
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.usage.CacheQuotaHint;
+import android.test.AndroidTestCase;
+import android.util.Pair;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.ByteArrayInputStream;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class CacheQuotaStrategyTest extends AndroidTestCase {
+    StringWriter mWriter;
+    FastXmlSerializer mOut;
+
+    @Before
+    public void setUp() throws Exception {
+        mWriter = new StringWriter();
+        mOut = new FastXmlSerializer();
+        mOut.setOutput(mWriter);
+    }
+
+    @Test
+    public void testEmptyWrite() throws Exception {
+        CacheQuotaStrategy.saveToXml(mOut, new ArrayList<>(), 0);
+        mOut.flush();
+
+        assertThat(mWriter.toString()).isEqualTo(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
+                        "<cache-info previousBytes=\"0\" />\n");
+    }
+
+    @Test
+    public void testWriteOneQuota() throws Exception {
+        ArrayList<CacheQuotaHint> requests = new ArrayList<>();
+        requests.add(buildCacheQuotaHint("uuid", 0, 100));
+
+        CacheQuotaStrategy.saveToXml(mOut, requests, 1000);
+        mOut.flush();
+
+        assertThat(mWriter.toString()).isEqualTo(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
+                "<cache-info previousBytes=\"1000\">\n"
+                        + "<quota uuid=\"uuid\" uid=\"0\" bytes=\"100\" />\n"
+                        + "</cache-info>\n");
+    }
+
+    @Test
+    public void testWriteMultipleQuotas() throws Exception {
+        ArrayList<CacheQuotaHint> requests = new ArrayList<>();
+        requests.add(buildCacheQuotaHint("uuid", 0, 100));
+        requests.add(buildCacheQuotaHint("uuid2", 10, 250));
+
+        CacheQuotaStrategy.saveToXml(mOut, requests, 1000);
+        mOut.flush();
+
+        assertThat(mWriter.toString()).isEqualTo(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
+                        "<cache-info previousBytes=\"1000\">\n"
+                        + "<quota uuid=\"uuid\" uid=\"0\" bytes=\"100\" />\n"
+                        + "<quota uuid=\"uuid2\" uid=\"10\" bytes=\"250\" />\n"
+                        + "</cache-info>\n");
+    }
+
+    @Test
+    public void testNullUuidDoesntCauseCrash() throws Exception {
+        ArrayList<CacheQuotaHint> requests = new ArrayList<>();
+        requests.add(buildCacheQuotaHint(null, 0, 100));
+        requests.add(buildCacheQuotaHint(null, 10, 250));
+
+        CacheQuotaStrategy.saveToXml(mOut, requests, 1000);
+        mOut.flush();
+
+        assertThat(mWriter.toString()).isEqualTo(
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
+                        "<cache-info previousBytes=\"1000\">\n"
+                        + "<quota uid=\"0\" bytes=\"100\" />\n"
+                        + "<quota uid=\"10\" bytes=\"250\" />\n"
+                        + "</cache-info>\n");
+    }
+
+    @Test
+    public void testReadMultipleQuotas() throws Exception {
+        String input = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+                + "<cache-info previousBytes=\"1000\">\n"
+                + "<quota uuid=\"uuid\" uid=\"0\" bytes=\"100\" />\n"
+                + "<quota uuid=\"uuid2\" uid=\"10\" bytes=\"250\" />\n"
+                + "</cache-info>\n";
+
+        Pair<Long, List<CacheQuotaHint>> output =
+                CacheQuotaStrategy.readFromXml(new ByteArrayInputStream(input.getBytes("UTF-8")));
+
+        assertThat(output.first).isEqualTo(1000);
+        assertThat(output.second).containsExactly(buildCacheQuotaHint("uuid", 0, 100),
+                buildCacheQuotaHint("uuid2", 10, 250));
+    }
+
+    private CacheQuotaHint buildCacheQuotaHint(String volumeUuid, int uid, long quota) {
+        return new CacheQuotaHint.Builder()
+                .setVolumeUuid(volumeUuid).setUid(uid).setQuota(quota).build();
+    }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index 04e5583..2ccaefc 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -138,19 +138,18 @@
 
     @Test
     public void testReparent() throws Exception {
+        final StackWindowController stackController =
+            createStackControllerOnDisplay(sDisplayContent);
         final TestTaskWindowContainerController taskController1 =
-                new TestTaskWindowContainerController(
-                        createStackControllerOnDisplay(sDisplayContent));
+                new TestTaskWindowContainerController(stackController);
         final TestAppWindowContainerController appWindowController1 = createAppWindowController(
                 taskController1);
         final TestTaskWindowContainerController taskController2 =
-                new TestTaskWindowContainerController(
-                        createStackControllerOnDisplay(sDisplayContent));
+                new TestTaskWindowContainerController(stackController);
         final TestAppWindowContainerController appWindowController2 = createAppWindowController(
                 taskController2);
         final TestTaskWindowContainerController taskController3 =
-                new TestTaskWindowContainerController(
-                        createStackControllerOnDisplay(sDisplayContent));
+                new TestTaskWindowContainerController(stackController);
 
         try {
             appWindowController1.reparent(taskController1, 0);
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 73ad7c2..e589bc7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -54,7 +54,7 @@
                 sDisplayContent, "exiting app");
         final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken;
         exitingAppToken.mIsExiting = true;
-        exitingAppToken.mTask.mStack.mExitingAppTokens.add(exitingAppToken);
+        exitingAppToken.getTask().mStack.mExitingAppTokens.add(exitingAppToken);
 
         assertForAllWindowsOrder(Arrays.asList(
                 sWallpaperWindow,
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 5dff997..58d277b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -49,7 +49,7 @@
         final ArraySet<Task> closingTasks = new ArraySet<>();
         sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
         assertEquals(1, closingTasks.size());
-        assertEquals(closingWindow.mAppToken.mTask, closingTasks.valueAt(0));
+        assertEquals(closingWindow.mAppToken.getTask(), closingTasks.valueAt(0));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 8392665..18c48b7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -158,21 +158,6 @@
         return win;
     }
 
-    /**
-     * Creates a window for a task on a the given {@param stackId}.
-     */
-    private WindowState createStackWindow(int stackId, String name) {
-        final StackWindowController stackController = createStackControllerOnStackOnDisplay(stackId,
-                sDisplayContent);
-        final TestTaskWindowContainerController taskController =
-                new TestTaskWindowContainerController(stackController);
-        TestAppWindowToken appWinToken = new TestAppWindowToken(sDisplayContent);
-        appWinToken.mTask = taskController.mContainer;
-        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, name);
-        win.mAppToken = appWinToken;
-        return win;
-    }
-
     /** Asserts that the first entry is greater than the second entry. */
     void assertGreaterThan(int first, int second) throws Exception {
         Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second);
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index ed1530a..89e68a6 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -55,6 +55,8 @@
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.storage.CacheQuotaStrategy;
 
+import java.io.IOException;
+
 public class StorageStatsService extends IStorageStatsManager.Stub {
     private static final String TAG = "StorageStatsService";
 
@@ -97,7 +99,7 @@
         invalidateMounts();
 
         mHandler = new H(IoThread.get().getLooper());
-        mHandler.sendEmptyMessageDelayed(H.MSG_CHECK_STORAGE_DELTA, DELAY_IN_MILLIS);
+        mHandler.sendEmptyMessageDelayed(H.MSG_LOAD_CACHED_QUOTAS_FROM_FILE, DELAY_IN_MILLIS);
 
         mStorage.registerListener(new StorageEventListener() {
             @Override
@@ -343,12 +345,14 @@
 
     private class H extends Handler {
         private static final int MSG_CHECK_STORAGE_DELTA = 100;
+        private static final int MSG_LOAD_CACHED_QUOTAS_FROM_FILE = 101;
         /**
          * By only triggering a re-calculation after the storage has changed sizes, we can avoid
          * recalculating quotas too often. Minimum change delta defines the percentage of change
          * we need to see before we recalculate.
          */
         private static final double MINIMUM_CHANGE_DELTA = 0.05;
+        private static final int UNSET = -1;
         private static final boolean DEBUG = false;
 
         private final StatFs mStats;
@@ -361,7 +365,6 @@
             mStats = new StatFs(Environment.getDataDirectory().getAbsolutePath());
             mPreviousBytes = mStats.getFreeBytes();
             mMinimumThresholdBytes = mStats.getTotalBytes() * MINIMUM_CHANGE_DELTA;
-            // TODO: Load cache quotas from a file to avoid re-doing work.
         }
 
         public void handleMessage(Message msg) {
@@ -378,7 +381,26 @@
                     long bytesDelta = Math.abs(mPreviousBytes - mStats.getFreeBytes());
                     if (bytesDelta > mMinimumThresholdBytes) {
                         mPreviousBytes = mStats.getFreeBytes();
-                        recalculateQuotas();
+                        recalculateQuotas(getInitializedStrategy());
+                    }
+                    sendEmptyMessageDelayed(MSG_CHECK_STORAGE_DELTA, DELAY_IN_MILLIS);
+                    break;
+                }
+                case MSG_LOAD_CACHED_QUOTAS_FROM_FILE: {
+                    CacheQuotaStrategy strategy = getInitializedStrategy();
+                    mPreviousBytes = UNSET;
+                    try {
+                        mPreviousBytes = strategy.setupQuotasFromFile();
+                    } catch (IOException e) {
+                        Slog.e(TAG, "An error occurred while reading the cache quota file.", e);
+                    } catch (IllegalStateException e) {
+                        Slog.e(TAG, "Cache quota XML file is malformed?", e);
+                    }
+
+                    // If errors occurred getting the quotas from disk, let's re-calc them.
+                    if (mPreviousBytes < 0) {
+                        mPreviousBytes = mStats.getFreeBytes();
+                        recalculateQuotas(strategy);
                     }
                     sendEmptyMessageDelayed(MSG_CHECK_STORAGE_DELTA, DELAY_IN_MILLIS);
                     break;
@@ -391,17 +413,18 @@
             }
         }
 
-        private void recalculateQuotas() {
+        private void recalculateQuotas(CacheQuotaStrategy strategy) {
             if (DEBUG) {
                 Slog.v(TAG, ">>> recalculating quotas ");
             }
 
+            strategy.recalculateQuotas();
+        }
+
+        private CacheQuotaStrategy getInitializedStrategy() {
             UsageStatsManagerInternal usageStatsManager =
                     LocalServices.getService(UsageStatsManagerInternal.class);
-            CacheQuotaStrategy strategy = new CacheQuotaStrategy(
-                    mContext, usageStatsManager, mInstaller);
-            // TODO: Save cache quotas to an XML file.
-            strategy.recalculateQuotas();
+            return new CacheQuotaStrategy(mContext, usageStatsManager, mInstaller);
         }
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index bae7cdc..a923fc3 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -117,7 +117,7 @@
     public UsbPortManager(Context context) {
         mContext = context;
         try {
-            boolean ret = IServiceManager.getService("manager")
+            boolean ret = IServiceManager.getService()
                     .registerForNotifications("android.hardware.usb@1.0::IUsb",
                     "", mServiceNotification);
             if (!ret) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 3f7c908..5bb479f 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -328,8 +328,15 @@
          */
         public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 0x00000080;
 
+        /**
+         * Indicates that the call is from a self-managed {@link ConnectionService}.
+         * <p>
+         * See also {@link Connection#PROPERTY_SELF_MANAGED}
+         */
+        public static final int PROPERTY_SELF_MANAGED = 0x00000100;
+
         //******************************************************************************************
-        // Next PROPERTY value: 0x00000100
+        // Next PROPERTY value: 0x00000200
         //******************************************************************************************
 
         private final String mTelecomCallId;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 2e144f2..359dcb1 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -373,6 +373,24 @@
             "android.telecom.INCLUDE_EXTERNAL_CALLS";
 
     /**
+     * A boolean meta-data value indicating whether an {@link InCallService} wants to be informed of
+     * calls which have the {@link Call.Details#PROPERTY_SELF_MANAGED} property.  A self-managed
+     * call is one which originates from a self-managed {@link ConnectionService} which has chosen
+     * to implement its own call user interface.  An {@link InCallService} implementation which
+     * would like to be informed of external calls should set this meta-data to {@code true} in the
+     * manifest registration of their {@link InCallService}.  By default, the {@link InCallService}
+     * will NOT be informed about self-managed calls.
+     * <p>
+     * An {@link InCallService} which receives self-managed calls is free to view and control the
+     * state of calls in the self-managed {@link ConnectionService}.  An example use-case is
+     * exposing these calls to a wearable or automotive device via its companion app.
+     * <p>
+     * See also {@link Connection#PROPERTY_SELF_MANAGED}.
+     */
+    public static final String METADATA_INCLUDE_SELF_MANAGED_CALLS =
+            "android.telecom.INCLUDE_SELF_MANAGED_CALLS";
+
+    /**
      * The dual tone multi-frequency signaling character sent to indicate the dialing system should
      * pause for a predefined period.
      */
@@ -1051,10 +1069,12 @@
 
     /**
      * Returns whether there is an ongoing phone call (can be in dialing, ringing, active or holding
-     * states).
+     * states) originating from either a manager or self-managed {@link ConnectionService}.
      * <p>
      * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
-     * </p>
+     *
+     * @return {@code true} if there is an ongoing call in either a managed or self-managed
+     *      {@link ConnectionService}, {@code false} otherwise.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public boolean isInCall() {
@@ -1069,6 +1089,31 @@
     }
 
     /**
+     * Returns whether there is an ongoing call originating from a managed
+     * {@link ConnectionService}.  An ongoing call can be in dialing, ringing, active or holding
+     * states.
+     * <p>
+     * If you also need to know if there are ongoing self-managed calls, use {@link #isInCall()}
+     * instead.
+     * <p>
+     * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+     *
+     * @return {@code true} if there is an ongoing call in a managed {@link ConnectionService},
+     *      {@code false} otherwise.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public boolean isInManagedCall() {
+        try {
+            if (isServiceConnected()) {
+                return getTelecomService().isInManagedCall(mContext.getOpPackageName());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException calling isInManagedCall().", e);
+        }
+        return false;
+    }
+
+    /**
      * Returns one of the following constants that represents the current state of Telecom:
      *
      * {@link TelephonyManager#CALL_STATE_RINGING}
@@ -1079,6 +1124,9 @@
      * {@link android.Manifest.permission#READ_PHONE_STATE} permission. This is intentional, to
      * preserve the behavior of {@link TelephonyManager#getCallState()}, which also did not require
      * the permission.
+     *
+     * Takes into consideration both managed and self-managed calls.
+     *
      * @hide
      */
     @SystemApi
@@ -1096,6 +1144,7 @@
     /**
      * Returns whether there currently exists is a ringing incoming-call.
      *
+     * @return {@code true} if there is a managed or self-managed ringing call.
      * @hide
      */
     @SystemApi
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index eb1cde3..8740224 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -165,6 +165,11 @@
     boolean isInCall(String callingPackage);
 
     /**
+     * @see TelecomServiceImpl#isInManagedCall
+     */
+    boolean isInManagedCall(String callingPackage);
+
+    /**
      * @see TelecomServiceImpl#isRinging
      */
     boolean isRinging(String callingPackage);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 81e87b3..932c276 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4041,9 +4041,8 @@
             }
         }
 
-        if (property.length() > SystemProperties.PROP_NAME_MAX
-                || propVal.length() > SystemProperties.PROP_VALUE_MAX) {
-            Rlog.d(TAG, "setTelephonyProperty: property to long phoneId=" + phoneId +
+        if (propVal.length() > SystemProperties.PROP_VALUE_MAX) {
+            Rlog.d(TAG, "setTelephonyProperty: property too long phoneId=" + phoneId +
                     " property=" + property + " value: " + value + " propVal=" + propVal);
             return;
         }
@@ -4300,6 +4299,45 @@
     }
 
     /**
+     * Returns an array of Forbidden PLMNs from the USIM App
+     * Returns null if the query fails.
+     *
+     *
+     * <p>Requires that the caller has READ_PRIVILEGED_PHONE_STATE
+     *
+     * @return an array of forbidden PLMNs or null if not available
+     */
+    public String[] getForbiddenPlmns() {
+      return getForbiddenPlmns(getSubId(), APPTYPE_USIM);
+    }
+
+    /**
+     * Returns an array of Forbidden PLMNs from the specified SIM App
+     * Returns null if the query fails.
+     *
+     *
+     * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
+     *
+     * @param subId subscription ID used for authentication
+     * @param appType the icc application type, like {@link #APPTYPE_USIM}
+     * @return fplmns an array of forbidden PLMNs
+     * @hide
+     */
+    public String[] getForbiddenPlmns(int subId, int appType) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null)
+                return null;
+            return telephony.getForbiddenPlmns(subId, appType);
+        } catch (RemoteException ex) {
+            return null;
+        } catch (NullPointerException ex) {
+            // This could happen before phone starts
+            return null;
+        }
+    }
+
+    /**
      * Get P-CSCF address from PCO after data connection is established or modified.
      * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
      * @return array of P-CSCF address
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 88daf64..e3816b6 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1278,7 +1278,6 @@
      */
     void setPolicyDataEnabled(boolean enabled, int subId);
 
-
     /**
      * Get Client request stats which will contain statistical information
      * on each request made by client.
@@ -1295,4 +1294,16 @@
      * @hide
      * */
     void setSimPowerStateForSlot(int slotId, boolean powerUp);
+
+    /**
+     * Returns a list of Forbidden PLMNs from the specified SIM App
+     * Returns null if the query fails.
+     *
+     *
+     * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
+     *
+     * @param subId subscription ID used for authentication
+     * @param appType the icc application type, like {@link #APPTYPE_USIM}
+     */
+    String[] getForbiddenPlmns(int subId, int appType);
 }
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index c3b740e..eafa6b7 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -411,6 +411,8 @@
     int RIL_REQUEST_GET_ACTIVITY_INFO = 135;
     int RIL_REQUEST_SET_ALLOWED_CARRIERS = 136;
     int RIL_REQUEST_GET_ALLOWED_CARRIERS = 137;
+    int RIL_REQUEST_SEND_DEVICE_STATE = 138;
+    int RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER = 139;
     int RIL_REQUEST_SET_SIM_CARD_POWER = 140;
 
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index d51d75e..29ba776 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -365,6 +365,12 @@
 
     /** @hide */
     @Override
+    public boolean isInstantApp(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
     public int getInstantAppCookieMaxSize() {
         throw new UnsupportedOperationException();
     }
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index b984bbf..684a101 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -36,21 +36,36 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.when;
 
-import android.net.ConnectivityManager;
-import android.net.NetworkCapabilities;
-
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Build.VERSION_CODES;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import org.junit.runner.RunWith;
+import org.junit.Before;
 import org.junit.Test;
-
-
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ConnectivityManagerTest {
+
+    @Mock Context mCtx;
+    @Mock IConnectivityManager mService;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
     static NetworkCapabilities verifyNetworkCapabilities(
             int legacyType, int transportType, int... capabilities) {
         final NetworkCapabilities nc = ConnectivityManager.networkCapabilitiesForType(legacyType);
@@ -173,4 +188,34 @@
         verifyUnrestrictedNetworkCapabilities(
                 ConnectivityManager.TYPE_ETHERNET, TRANSPORT_ETHERNET);
     }
+
+    @Test
+    public void testNoDoubleCallbackRegistration() throws Exception {
+        ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
+        NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
+        NetworkCallback callback = new ConnectivityManager.NetworkCallback();
+        ApplicationInfo info = new ApplicationInfo();
+        info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
+
+        when(mCtx.getApplicationInfo()).thenReturn(info);
+        when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt())).thenReturn(request);
+
+        manager.requestNetwork(request, callback);
+
+        // Callback is already registered, reregistration should fail.
+        Class<IllegalArgumentException> wantException = IllegalArgumentException.class;
+        expectThrowable(() -> manager.requestNetwork(request, callback), wantException);
+    }
+
+    static void expectThrowable(Runnable block, Class<? extends Throwable> throwableType) {
+        try {
+            block.run();
+        } catch (Throwable t) {
+            if (t.getClass().equals(throwableType)) {
+                return;
+            }
+            fail("expected exception of type " + throwableType + ", but was " + t.getClass());
+        }
+        fail("expected exception of type " + throwableType);
+    }
 }
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 0e031e7..15648bd 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -309,6 +309,8 @@
     CATEGORY_ATTR = 0x010103e8,
     BANNER_ATTR = 0x10103f2,
     ISGAME_ATTR = 0x10103f4,
+    REQUIRED_FEATURE_ATTR = 0x1010557,
+    REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
 };
 
 String8 getComponentName(String8 &pkgName, String8 &componentName) {
@@ -366,11 +368,19 @@
     printf("\n");
 }
 
-static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) {
+static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1,
+        const String8& requiredFeature = String8::empty(),
+        const String8& requiredNotFeature = String8::empty()) {
     printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string());
     if (maxSdkVersion != -1) {
          printf(" maxSdkVersion='%d'", maxSdkVersion);
     }
+    if (requiredFeature.length() > 0) {
+         printf(" requiredFeature='%s'", requiredFeature.string());
+    }
+    if (requiredNotFeature.length() > 0) {
+         printf(" requiredNotFeature='%s'", requiredNotFeature.string());
+    }
     printf("\n");
 
     if (optional) {
@@ -1545,6 +1555,10 @@
 
                         const int32_t maxSdkVersion =
                                 AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, -1);
+                        const String8 requiredFeature = AaptXml::getAttribute(tree,
+                                REQUIRED_FEATURE_ATTR, &error);
+                        const String8 requiredNotFeature = AaptXml::getAttribute(tree,
+                                REQUIRED_NOT_FEATURE_ATTR, &error);
 
                         if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
                             hasWriteExternalStoragePermission = true;
@@ -1565,7 +1579,7 @@
 
                         printUsesPermission(name,
                                 AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
-                                maxSdkVersion);
+                                maxSdkVersion, requiredFeature, requiredNotFeature);
 
                     } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
index ffc2de1..ec3d75e 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -76,7 +76,7 @@
             <View xmlns:test="http://com.test"
                   attr="hey">
               <Layout test:hello="hi" />
-              <Layout>Some text</Layout>
+              <Layout>Some text\\</Layout>
             </View>)EOF");
 
   android::ResXMLTree tree;
@@ -128,7 +128,7 @@
 
   ASSERT_EQ(tree.next(), android::ResXMLTree::TEXT);
   const char16_t* text = tree.getText(&len);
-  EXPECT_EQ(StringPiece16(text, len), u"Some text");
+  EXPECT_EQ(StringPiece16(text, len), u"Some text\\");
 
   ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
   ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index f8fa80e..7210d21 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -164,6 +164,7 @@
   StringBuilder& Append(const android::StringPiece& str);
   const std::string& ToString() const;
   const std::string& Error() const;
+  bool IsEmpty() const;
 
   // When building StyledStrings, we need UTF-16 indices into the string,
   // which is what the Java layer expects when dealing with java
@@ -185,6 +186,8 @@
 
 inline const std::string& StringBuilder::Error() const { return error_; }
 
+inline bool StringBuilder::IsEmpty() const { return str_.empty(); }
+
 inline size_t StringBuilder::Utf16Len() const { return utf16_len_; }
 
 inline StringBuilder::operator bool() const { return error_.empty(); }
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index fab2f19..d9ea1bc 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -18,7 +18,6 @@
 
 #include <expat.h>
 
-#include <cassert>
 #include <memory>
 #include <stack>
 #include <string>
@@ -41,6 +40,8 @@
   std::unique_ptr<xml::Node> root;
   std::stack<xml::Node*> node_stack;
   std::string pending_comment;
+  std::unique_ptr<xml::Text> last_text_node;
+  util::StringBuilder pending_text;
 };
 
 /**
@@ -62,6 +63,19 @@
   }
 }
 
+static void FinishPendingText(Stack* stack) {
+  if (stack->last_text_node != nullptr) {
+    if (!stack->pending_text.IsEmpty()) {
+      stack->last_text_node->text = stack->pending_text.ToString();
+      stack->pending_text = {};
+      stack->node_stack.top()->AppendChild(std::move(stack->last_text_node));
+    } else {
+      // Drop an empty text node.
+      stack->last_text_node = nullptr;
+    }
+  }
+}
+
 static void AddToStack(Stack* stack, XML_Parser parser,
                        std::unique_ptr<Node> node) {
   node->line_number = XML_GetCurrentLineNumber(parser);
@@ -83,6 +97,7 @@
                                           const char* uri) {
   XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
   Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+  FinishPendingText(stack);
 
   std::unique_ptr<Namespace> ns = util::make_unique<Namespace>();
   if (prefix) {
@@ -99,6 +114,7 @@
 static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix) {
   XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
   Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+  FinishPendingText(stack);
 
   CHECK(!stack->node_stack.empty());
   stack->node_stack.pop();
@@ -113,6 +129,7 @@
                                         const char** attrs) {
   XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
   Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+  FinishPendingText(stack);
 
   std::unique_ptr<Element> el = util::make_unique<Element>();
   SplitName(name, &el->namespace_uri, &el->name);
@@ -120,7 +137,9 @@
   while (*attrs) {
     Attribute attribute;
     SplitName(*attrs++, &attribute.namespace_uri, &attribute.name);
-    attribute.value = *attrs++;
+    util::StringBuilder builder;
+    builder.Append(*attrs++);
+    attribute.value = builder.ToString();
 
     // Insert in sorted order.
     auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(),
@@ -135,41 +154,38 @@
 static void XMLCALL EndElementHandler(void* user_data, const char* name) {
   XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
   Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+  FinishPendingText(stack);
 
   CHECK(!stack->node_stack.empty());
   // stack->nodeStack.top()->comment = std::move(stack->pendingComment);
   stack->node_stack.pop();
 }
 
-static void XMLCALL CharacterDataHandler(void* user_data, const char* s,
-                                         int len) {
+static void XMLCALL CharacterDataHandler(void* user_data, const char* s, int len) {
   XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
   Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
 
-  if (!s || len <= 0) {
+  const StringPiece str(s, len);
+  if (str.empty()) {
     return;
   }
 
   // See if we can just append the text to a previous text node.
-  if (!stack->node_stack.empty()) {
-    Node* currentParent = stack->node_stack.top();
-    if (!currentParent->children.empty()) {
-      Node* last_child = currentParent->children.back().get();
-      if (Text* text = NodeCast<Text>(last_child)) {
-        text->text.append(s, len);
-        return;
-      }
-    }
+  if (stack->last_text_node != nullptr) {
+    stack->pending_text.Append(str);
+    return;
   }
 
-  std::unique_ptr<Text> text = util::make_unique<Text>();
-  text->text.assign(s, len);
-  AddToStack(stack, parser, std::move(text));
+  stack->last_text_node = util::make_unique<Text>();
+  stack->last_text_node->line_number = XML_GetCurrentLineNumber(parser);
+  stack->last_text_node->column_number = XML_GetCurrentColumnNumber(parser);
+  stack->pending_text.Append(str);
 }
 
 static void XMLCALL CommentDataHandler(void* user_data, const char* comment) {
   XML_Parser parser = reinterpret_cast<XML_Parser>(user_data);
   Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+  FinishPendingText(stack);
 
   if (!stack->pending_comment.empty()) {
     stack->pending_comment += '\n';
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index a414afe..0fc3cec6 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -49,4 +49,23 @@
   EXPECT_EQ(ns->namespace_prefix, "android");
 }
 
+TEST(XmlDomTest, HandleEscapes) {
+  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(
+      R"EOF(<shortcode pattern="\\d{5}">\\d{5}</shortcode>)EOF");
+
+  xml::Element* el = xml::FindRootElement(doc->root.get());
+  ASSERT_NE(nullptr, el);
+
+  xml::Attribute* attr = el->FindAttribute({}, "pattern");
+  ASSERT_NE(nullptr, attr);
+
+  EXPECT_EQ("\\d{5}", attr->value);
+
+  ASSERT_EQ(1u, el->children.size());
+
+  xml::Text* text = xml::NodeCast<xml::Text>(el->children[0].get());
+  ASSERT_NE(nullptr, text);
+  EXPECT_EQ("\\d{5}", text->text);
+}
+
 }  // namespace aapt
diff --git a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
index fad35d2..8130bc2 100644
--- a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
+++ b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
@@ -52,6 +52,8 @@
             throw new IllegalArgumentException("Outline is not a rect shadow");
         }
 
+        // TODO replacing the algorithm here to create better shadow
+
         float shadowSize = elevationToShadow(elevation);
         int saved = modifyCanvas(canvas, shadowSize);
         if (saved == -1) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index 0039476..2274b90 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -321,6 +321,11 @@
     }
 
     @Override
+    public boolean isInstantApp(String packageName) {
+        return false;
+    }
+
+    @Override
     public int getInstantAppCookieMaxSize() {
         return 0;
     }
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png
new file mode 100644
index 0000000..4f3ed60
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_test.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_test.xml
new file mode 100644
index 0000000..59dbbec
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/shadows_test.xml
@@ -0,0 +1,97 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" >
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+
+        <Button
+            android:layout_marginLeft="40dp"
+            android:layout_width="48dp"
+            android:layout_height="40dp"
+            android:layout_alignParentLeft="true"
+            android:layout_centerVertical="true"
+            android:elevation="48dp"
+            android:stateListAnimator="@null"/>
+
+        <Button
+            android:layout_marginRight="40dp"
+            android:layout_width="48dp"
+            android:layout_height="40dp"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:elevation="48dp"
+            android:stateListAnimator="@null"/>
+
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+
+        <Button
+            android:layout_marginLeft="40dp"
+            android:layout_width="48dp"
+            android:layout_height="40dp"
+            android:layout_alignParentLeft="true"
+            android:layout_centerVertical="true"
+            android:elevation="0dp"
+            android:stateListAnimator="@null"/>
+
+        <Button
+            android:layout_marginRight="40dp"
+            android:layout_width="48dp"
+            android:layout_height="40dp"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:elevation="100dp"
+            android:stateListAnimator="@null"/>
+
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1">
+
+        <Button
+            android:layout_marginLeft="40dp"
+            android:layout_width="48dp"
+            android:layout_height="40dp"
+            android:layout_alignParentLeft="true"
+            android:layout_centerVertical="true"
+            android:elevation="12dp"
+            android:stateListAnimator="@null"/>
+
+        <Button
+            android:layout_marginRight="40dp"
+            android:layout_width="48dp"
+            android:layout_height="40dp"
+            android:layout_alignParentRight="true"
+            android:layout_centerVertical="true"
+            android:elevation="36dp"
+            android:stateListAnimator="@null"/>
+
+    </RelativeLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index b15ee95..c83d2e4 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -34,7 +34,7 @@
 @SuiteClasses({
         RenderTests.class, LayoutParserWrapperTest.class,
         BridgeXmlBlockParserTest.class, BridgeXmlPullAttributesTest.class,
-        Matrix_DelegateTest.class, TestDelegates.class
+        Matrix_DelegateTest.class, TestDelegates.class, PerformanceTests.class
 })
 public class Main {
 }
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
index 9e60f0f..7199781 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -424,4 +424,9 @@
         assertNotEquals(0, outValue.data);
         assertTrue(sRenderMessages.isEmpty());
     }
+
+    @Test
+    public void testRectangleShadow() throws Exception {
+        renderAndVerify("shadows_test.xml", "shadows_test.png");
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index a4ab64f..a1099f8 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -376,7 +376,9 @@
 
     /**
      * Flag indicating if this network is provided by a home Passpoint provider or a roaming
-     * Passpoint provider.
+     * Passpoint provider.  This flag will be {@code true} if this network is provided by
+     * a home Passpoint provider and {@code false} if is provided by a roaming Passpoint provider
+     * or is a non-Passpoint network.
      */
     public boolean isHomeProviderNetwork;
 
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index f790332..4268f24 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -236,11 +236,11 @@
         public static final int TTLS    = 2;
         /** EAP-Password */
         public static final int PWD     = 3;
-        /** EAP-Subscriber Identity Module */
+        /** EAP-Subscriber Identity Module [RFC-4186] */
         public static final int SIM     = 4;
-        /** EAP-Authentication and Key Agreement */
+        /** EAP-Authentication and Key Agreement [RFC-4187] */
         public static final int AKA     = 5;
-        /** EAP-Authentication and Key Agreement Prime */
+        /** EAP-Authentication and Key Agreement Prime [RFC-5448] */
         public static final int AKA_PRIME = 6;
         /** Hotspot 2.0 r2 OSEN */
         public static final int UNAUTH_TLS = 7;
@@ -263,11 +263,11 @@
         public static final int MSCHAPV2    = 3;
         /** Generic Token Card */
         public static final int GTC         = 4;
-        /** EAP-Subscriber Identity Module */
+        /** EAP-Subscriber Identity Module [RFC-4186] */
         public static final int SIM         = 5;
-        /** EAP-Authentication and Key Agreement */
+        /** EAP-Authentication and Key Agreement [RFC-4187] */
         public static final int AKA         = 6;
-        /** EAP-Authentication and Key Agreement Prime */
+        /** EAP-Authentication and Key Agreement Prime [RFC-5448] */
         public static final int AKA_PRIME   = 7;
         private static final String AUTH_PREFIX = "auth=";
         private static final String AUTHEAP_PREFIX = "autheap=";
@@ -756,8 +756,8 @@
      * key entry when the config is saved and removing the key entry when
      * the config is removed.
 
-     * @param privateKey
-     * @param clientCertificate
+     * @param privateKey a PrivateKey instance for the end certificate.
+     * @param clientCertificate an X509Certificate representing the end certificate.
      * @throws IllegalArgumentException for an invalid key or certificate.
      */
     public void setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate) {
@@ -775,9 +775,11 @@
      * with this configuration.  The framework takes care of installing the
      * key entry when the config is saved and removing the key entry when
      * the config is removed.
-
-     * @param privateKey
-     * @param clientCertificateChain
+     *
+     * @param privateKey a PrivateKey instance for the end certificate.
+     * @param clientCertificateChain an array of X509Certificate instances which starts with
+     *         end certificate and continues with additional CA certificates necessary to
+     *         link the end certificate with some root certificate known by the authenticator.
      * @throws IllegalArgumentException for an invalid key or certificate.
      */
     public void setClientKeyEntryWithCertificateChain(PrivateKey privateKey,
@@ -835,7 +837,15 @@
     }
 
     /**
-     * Get the complete client certificate chain
+     * Get the complete client certificate chain in the same order as it was last supplied.
+     *
+     * <p>If the chain was last supplied by a call to
+     * {@link #setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate)}
+     * with a non-null * certificate instance, a single-element array containing the certificate
+     * will be * returned. If {@link #setClientKeyEntryWithCertificateChain(
+     * java.security.PrivateKey, java.security.cert.X509Certificate[])} was last called with a
+     * non-empty array, this array will be returned in the same order as it was supplied.
+     * Otherwise, {@code null} will be returned.
      *
      * @return X.509 client certificates
      */
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 620759d..2388841 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -122,12 +122,22 @@
         private static final int MAX_PASSWORD_BYTES = 255;
 
         /**
+         * Supported authentication methods.
+         * @hide
+         */
+        public static final String AUTH_METHOD_PAP = "PAP";
+        /** @hide */
+        public static final String AUTH_METHOD_MSCHAP = "MS-CHAP";
+        /** @hide */
+        public static final String AUTH_METHOD_MSCHAPV2 = "MS-CHAP-V2";
+
+        /**
          * Supported Non-EAP inner methods.  Refer to
          * Credential/UsernamePassword/EAPMethod/InnerEAPType in Hotspot 2.0 Release 2 Technical
          * Specification Section 9.1 for more info.
          */
-        private static final Set<String> SUPPORTED_AUTH =
-                new HashSet<String>(Arrays.asList("PAP", "CHAP", "MS-CHAP", "MS-CHAP-V2"));
+        private static final Set<String> SUPPORTED_AUTH = new HashSet<String>(
+                Arrays.asList(AUTH_METHOD_PAP, AUTH_METHOD_MSCHAP, AUTH_METHOD_MSCHAPV2));
 
         /**
          * Username of the credential.
@@ -348,8 +358,9 @@
     public static final class CertificateCredential implements Parcelable {
         /**
          * Supported certificate types.
+         * @hide
          */
-        private static final String CERT_TYPE_X509V3 = "x509v3";
+        public static final String CERT_TYPE_X509V3 = "x509v3";
 
         /**
          * Certificate SHA-256 fingerprint length.
diff --git a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
index c4d2d32..d0aedba 100644
--- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
@@ -89,11 +89,29 @@
     @Test
     public void testSetClientKeyEntryWithNull() {
         mEnterpriseConfig.setClientKeyEntry(null, null);
-        assertEquals(null, mEnterpriseConfig.getClientCertificateChain());
-        assertEquals(null, mEnterpriseConfig.getClientCertificate());
+        assertNull(mEnterpriseConfig.getClientCertificateChain());
+        assertNull(mEnterpriseConfig.getClientCertificate());
         mEnterpriseConfig.setClientKeyEntryWithCertificateChain(null, null);
-        assertEquals(null, mEnterpriseConfig.getClientCertificateChain());
-        assertEquals(null, mEnterpriseConfig.getClientCertificate());
+        assertNull(mEnterpriseConfig.getClientCertificateChain());
+        assertNull(mEnterpriseConfig.getClientCertificate());
+
+        // Setting the client certificate to null should clear the existing chain.
+        PrivateKey clientKey = FakeKeys.RSA_KEY1;
+        X509Certificate clientCert0 = FakeKeys.CLIENT_CERT;
+        X509Certificate clientCert1 = FakeKeys.CA_CERT1;
+        mEnterpriseConfig.setClientKeyEntry(clientKey, clientCert0);
+        assertNotNull(mEnterpriseConfig.getClientCertificate());
+        mEnterpriseConfig.setClientKeyEntry(null, null);
+        assertNull(mEnterpriseConfig.getClientCertificate());
+        assertNull(mEnterpriseConfig.getClientCertificateChain());
+
+        // Setting the chain to null should clear the existing chain.
+        X509Certificate[] clientChain = new X509Certificate[] {clientCert0, clientCert1};
+        mEnterpriseConfig.setClientKeyEntryWithCertificateChain(clientKey, clientChain);
+        assertNotNull(mEnterpriseConfig.getClientCertificateChain());
+        mEnterpriseConfig.setClientKeyEntryWithCertificateChain(null, null);
+        assertNull(mEnterpriseConfig.getClientCertificate());
+        assertNull(mEnterpriseConfig.getClientCertificateChain());
     }
 
     @Test